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 */ 017package org.apache.logging.log4j.core.appender.db.jdbc; 018 019import java.io.PrintWriter; 020import java.lang.reflect.Method; 021import java.sql.Connection; 022import java.sql.SQLException; 023 024import javax.sql.DataSource; 025 026import org.apache.logging.log4j.Logger; 027import org.apache.logging.log4j.core.config.plugins.Plugin; 028import org.apache.logging.log4j.core.config.plugins.PluginAttribute; 029import org.apache.logging.log4j.core.config.plugins.PluginFactory; 030import org.apache.logging.log4j.core.util.Loader; 031import org.apache.logging.log4j.status.StatusLogger; 032import 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) 039public 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}