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.helpers.Strings; 031import org.apache.logging.log4j.status.StatusLogger; 032 033/** 034 * A {@link JDBCAppender} connection source that uses a public static factory method to obtain a {@link Connection} or 035 * {@link DataSource}. 036 */ 037@Plugin(name = "ConnectionFactory", category = "Core", elementType = "connectionSource", printObject = true) 038public final class FactoryMethodConnectionSource implements ConnectionSource { 039 private static final Logger LOGGER = StatusLogger.getLogger(); 040 041 private final DataSource dataSource; 042 private final String description; 043 044 private FactoryMethodConnectionSource(final DataSource dataSource, final String className, final String methodName, 045 final String returnType) { 046 this.dataSource = dataSource; 047 this.description = "factory{ public static " + returnType + " " + className + "." + methodName + "() }"; 048 } 049 050 @Override 051 public Connection getConnection() throws SQLException { 052 return this.dataSource.getConnection(); 053 } 054 055 @Override 056 public String toString() { 057 return this.description; 058 } 059 060 /** 061 * Factory method for creating a connection source within the plugin manager. 062 * 063 * @param className The name of a public class that contains a static method capable of returning either a 064 * {@link DataSource} or a {@link Connection}. 065 * @param methodName The name of the public static method on the aforementioned class that returns the data source 066 * or connection. If this method returns a {@link Connection}, it should return a new connection 067 * every call. 068 * @return the created connection source. 069 */ 070 @PluginFactory 071 public static FactoryMethodConnectionSource createConnectionSource( 072 @PluginAttribute("class") final String className, 073 @PluginAttribute("method") final String methodName) { 074 if (Strings.isEmpty(className) || Strings.isEmpty(methodName)) { 075 LOGGER.error("No class name or method name specified for the connection factory method."); 076 return null; 077 } 078 079 final Method method; 080 try { 081 final Class<?> factoryClass = Class.forName(className); 082 method = factoryClass.getMethod(methodName); 083 } catch (final Exception e) { 084 LOGGER.error(e.toString(), e); 085 return null; 086 } 087 088 final Class<?> returnType = method.getReturnType(); 089 String returnTypeString = returnType.getName(); 090 DataSource dataSource; 091 if (returnType == DataSource.class) { 092 try { 093 dataSource = (DataSource) method.invoke(null); 094 returnTypeString += "[" + dataSource + "]"; 095 } catch (final Exception e) { 096 LOGGER.error(e.toString(), e); 097 return null; 098 } 099 } else if (returnType == Connection.class) { 100 dataSource = new DataSource() { 101 @Override 102 public Connection getConnection() throws SQLException { 103 try { 104 return (Connection) method.invoke(null); 105 } catch (final Exception e) { 106 throw new SQLException("Failed to obtain connection from factory method.", e); 107 } 108 } 109 110 @Override 111 public Connection getConnection(final String username, final String password) throws SQLException { 112 throw new UnsupportedOperationException(); 113 } 114 115 @Override 116 public int getLoginTimeout() throws SQLException { 117 throw new UnsupportedOperationException(); 118 } 119 120 @Override 121 public PrintWriter getLogWriter() throws SQLException { 122 throw new UnsupportedOperationException(); 123 } 124 125 // method must be present to compile on Java 7! 126 // @Override must be absent to compile on Java 6! 127 @SuppressWarnings("unused") 128 public java.util.logging.Logger getParentLogger() { 129 throw new UnsupportedOperationException(); 130 } 131 132 @Override 133 public boolean isWrapperFor(final Class<?> iface) throws SQLException { 134 return false; 135 } 136 137 @Override 138 public void setLoginTimeout(final int seconds) throws SQLException { 139 throw new UnsupportedOperationException(); 140 } 141 142 @Override 143 public void setLogWriter(final PrintWriter out) throws SQLException { 144 throw new UnsupportedOperationException(); 145 } 146 147 @Override 148 public <T> T unwrap(final Class<T> iface) throws SQLException { 149 return null; 150 } 151 }; 152 } else { 153 LOGGER.error("Method [{}.{}()] returns unsupported type [{}].", className, methodName, 154 returnType.getName()); 155 return null; 156 } 157 158 return new FactoryMethodConnectionSource(dataSource, className, methodName, returnTypeString); 159 } 160}