001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017 package org.apache.logging.log4j.core.appender.db.jdbc;
018
019 import java.io.PrintWriter;
020 import java.lang.reflect.Method;
021 import java.sql.Connection;
022 import java.sql.SQLException;
023
024 import javax.sql.DataSource;
025
026 import org.apache.logging.log4j.Logger;
027 import org.apache.logging.log4j.core.config.plugins.Plugin;
028 import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
029 import org.apache.logging.log4j.core.config.plugins.PluginFactory;
030 import org.apache.logging.log4j.core.util.Loader;
031 import org.apache.logging.log4j.status.StatusLogger;
032 import org.apache.logging.log4j.util.Strings;
033
034 /**
035 * A {@link JdbcAppender} connection source that uses a public static factory method to obtain a {@link Connection} or
036 * {@link DataSource}.
037 */
038 @Plugin(name = "ConnectionFactory", category = "Core", elementType = "connectionSource", printObject = true)
039 public final class FactoryMethodConnectionSource implements ConnectionSource {
040 private static final Logger LOGGER = StatusLogger.getLogger();
041
042 private final DataSource dataSource;
043 private final String description;
044
045 private FactoryMethodConnectionSource(final DataSource dataSource, final String className, final String methodName,
046 final String returnType) {
047 this.dataSource = dataSource;
048 this.description = "factory{ public static " + returnType + ' ' + className + '.' + methodName + "() }";
049 }
050
051 @Override
052 public Connection getConnection() throws SQLException {
053 return this.dataSource.getConnection();
054 }
055
056 @Override
057 public String toString() {
058 return this.description;
059 }
060
061 /**
062 * Factory method for creating a connection source within the plugin manager.
063 *
064 * @param className The name of a public class that contains a static method capable of returning either a
065 * {@link DataSource} or a {@link Connection}.
066 * @param methodName The name of the public static method on the aforementioned class that returns the data source
067 * or connection. If this method returns a {@link Connection}, it should return a new connection
068 * every call.
069 * @return the created connection source.
070 */
071 @PluginFactory
072 public static FactoryMethodConnectionSource createConnectionSource(
073 @PluginAttribute("class") final String className,
074 @PluginAttribute("method") final String methodName) {
075 if (Strings.isEmpty(className) || Strings.isEmpty(methodName)) {
076 LOGGER.error("No class name or method name specified for the connection factory method.");
077 return null;
078 }
079
080 final Method method;
081 try {
082 final Class<?> factoryClass = Loader.loadClass(className);
083 method = factoryClass.getMethod(methodName);
084 } catch (final Exception e) {
085 LOGGER.error(e.toString(), e);
086 return null;
087 }
088
089 final Class<?> returnType = method.getReturnType();
090 String returnTypeString = returnType.getName();
091 DataSource dataSource;
092 if (returnType == DataSource.class) {
093 try {
094 dataSource = (DataSource) method.invoke(null);
095 returnTypeString += "[" + dataSource + ']';
096 } catch (final Exception e) {
097 LOGGER.error(e.toString(), e);
098 return null;
099 }
100 } else if (returnType == Connection.class) {
101 dataSource = new DataSource() {
102 @Override
103 public Connection getConnection() throws SQLException {
104 try {
105 return (Connection) method.invoke(null);
106 } catch (final Exception e) {
107 throw new SQLException("Failed to obtain connection from factory method.", e);
108 }
109 }
110
111 @Override
112 public Connection getConnection(final String username, final String password) throws SQLException {
113 throw new UnsupportedOperationException();
114 }
115
116 @Override
117 public int getLoginTimeout() throws SQLException {
118 throw new UnsupportedOperationException();
119 }
120
121 @Override
122 public PrintWriter getLogWriter() throws SQLException {
123 throw new UnsupportedOperationException();
124 }
125
126 // method must be present to compile on Java 7!
127 // @Override must be absent to compile on Java 6!
128 @SuppressWarnings("unused")
129 public java.util.logging.Logger getParentLogger() {
130 throw new UnsupportedOperationException();
131 }
132
133 @Override
134 public boolean isWrapperFor(final Class<?> iface) throws SQLException {
135 return false;
136 }
137
138 @Override
139 public void setLoginTimeout(final int seconds) throws SQLException {
140 throw new UnsupportedOperationException();
141 }
142
143 @Override
144 public void setLogWriter(final PrintWriter out) throws SQLException {
145 throw new UnsupportedOperationException();
146 }
147
148 @Override
149 public <T> T unwrap(final Class<T> iface) throws SQLException {
150 return null;
151 }
152 };
153 } else {
154 LOGGER.error("Method [{}.{}()] returns unsupported type [{}].", className, methodName,
155 returnType.getName());
156 return null;
157 }
158
159 return new FactoryMethodConnectionSource(dataSource, className, methodName, returnTypeString);
160 }
161 }