View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.appender.db.jdbc;
18  
19  import java.io.PrintWriter;
20  import java.lang.reflect.Method;
21  import java.sql.Connection;
22  import java.sql.SQLException;
23  import javax.sql.DataSource;
24  
25  import org.apache.logging.log4j.Logger;
26  import org.apache.logging.log4j.core.config.plugins.Plugin;
27  import org.apache.logging.log4j.core.config.plugins.PluginAttr;
28  import org.apache.logging.log4j.core.config.plugins.PluginFactory;
29  import org.apache.logging.log4j.status.StatusLogger;
30  
31  /**
32   * A {@link JDBCAppender} connection source that uses a public static factory method to obtain a {@link Connection} or
33   * {@link DataSource}.
34   */
35  @Plugin(name = "ConnectionFactory", category = "Core", elementType = "connectionSource", printObject = true)
36  public final class FactoryMethodConnectionSource implements ConnectionSource {
37      private static final Logger LOGGER = StatusLogger.getLogger();
38  
39      private final DataSource dataSource;
40      private final String description;
41  
42      private FactoryMethodConnectionSource(final DataSource dataSource, final String className, final String methodName,
43                                            final String returnType) {
44          this.dataSource = dataSource;
45          this.description = "factory{ public static " + returnType + " " + className + "." + methodName + "() }";
46      }
47  
48      @Override
49      public Connection getConnection() throws SQLException {
50          return this.dataSource.getConnection();
51      }
52  
53      @Override
54      public String toString() {
55          return this.description;
56      }
57  
58      /**
59       * Factory method for creating a connection source within the plugin manager.
60       *
61       * @param className The name of a public class that contains a static method capable of returning either a
62       *                  {@link DataSource} or a {@link Connection}.
63       * @param methodName The name of the public static method on the aforementioned class that returns the data source
64       *                   or connection. If this method returns a {@link Connection}, it should return a new connection
65       *                   every call.
66       * @return the created connection source.
67       */
68      @PluginFactory
69      public static FactoryMethodConnectionSource createConnectionSource(@PluginAttr("class") final String className,
70                                                                         @PluginAttr("method") final String methodName) {
71          if (className == null || className.length() == 0 || methodName == null || methodName.length() == 0) {
72              LOGGER.error("No class name or method name specified for the connection factory method.");
73              return null;
74          }
75  
76          final Method method;
77          try {
78              final Class<?> factoryClass = Class.forName(className);
79              method = factoryClass.getMethod(methodName);
80          } catch (final Exception e) {
81              LOGGER.error(e.toString(), e);
82              return null;
83          }
84  
85          final Class<?> returnType = method.getReturnType();
86          String returnTypeString = returnType.getName();
87          DataSource dataSource;
88          if (returnType == DataSource.class) {
89              try {
90                  dataSource = (DataSource) method.invoke(null);
91                  returnTypeString += "[" + dataSource + "]";
92              } catch (final Exception e) {
93                  LOGGER.error(e.toString(), e);
94                  return null;
95              }
96          } else if (returnType == Connection.class) {
97              dataSource = new DataSource() {
98                  @Override
99                  public Connection getConnection() throws SQLException {
100                     try {
101                         return (Connection) method.invoke(null);
102                     } catch (final Exception e) {
103                         throw new SQLException("Failed to obtain connection from factory method.", e);
104                     }
105                 }
106 
107                 @Override
108                 public Connection getConnection(final String username, final String password) throws SQLException {
109                     throw new UnsupportedOperationException();
110                 }
111 
112                 @Override
113                 public int getLoginTimeout() throws SQLException {
114                     throw new UnsupportedOperationException();
115                 }
116 
117                 @Override
118                 public PrintWriter getLogWriter() throws SQLException {
119                     throw new UnsupportedOperationException();
120                 }
121 
122                 // method must be present to compile on Java 7, @Override must be absent to compile on Java 6
123                 @SuppressWarnings("unused")
124                 public java.util.logging.Logger getParentLogger() {
125                     throw new UnsupportedOperationException();
126                 }
127 
128                 @Override
129                 public boolean isWrapperFor(final Class<?> iface) throws SQLException {
130                     return false;
131                 }
132 
133                 @Override
134                 public void setLoginTimeout(final int seconds) throws SQLException {
135                     throw new UnsupportedOperationException();
136                 }
137 
138                 @Override
139                 public void setLogWriter(final PrintWriter out) throws SQLException {
140                     throw new UnsupportedOperationException();
141                 }
142 
143                 @Override
144                 public <T> T unwrap(final Class<T> iface) throws SQLException {
145                     return null;
146                 }
147             };
148         } else {
149             LOGGER.error("Method [{}.{}()] returns unsupported type [{}].", className, methodName,
150                     returnType.getName());
151             return null;
152         }
153 
154         return new FactoryMethodConnectionSource(dataSource, className, methodName, returnTypeString);
155     }
156 }