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 import javax.sql.DataSource; 024 025 import org.apache.logging.log4j.Logger; 026 import org.apache.logging.log4j.core.config.plugins.Plugin; 027 import org.apache.logging.log4j.core.config.plugins.PluginAttr; 028 import org.apache.logging.log4j.core.config.plugins.PluginFactory; 029 import org.apache.logging.log4j.status.StatusLogger; 030 031 /** 032 * A {@link JDBCAppender} connection source that uses a public static factory method to obtain a {@link Connection} or 033 * {@link DataSource}. 034 */ 035 @Plugin(name = "ConnectionFactory", category = "Core", elementType = "connectionSource", printObject = true) 036 public final class FactoryMethodConnectionSource implements ConnectionSource { 037 private static final Logger LOGGER = StatusLogger.getLogger(); 038 039 private final DataSource dataSource; 040 private final String description; 041 042 private FactoryMethodConnectionSource(final DataSource dataSource, final String className, final String methodName, 043 final String returnType) { 044 this.dataSource = dataSource; 045 this.description = "factory{ public static " + returnType + " " + className + "." + methodName + "() }"; 046 } 047 048 @Override 049 public Connection getConnection() throws SQLException { 050 return this.dataSource.getConnection(); 051 } 052 053 @Override 054 public String toString() { 055 return this.description; 056 } 057 058 /** 059 * Factory method for creating a connection source within the plugin manager. 060 * 061 * @param className The name of a public class that contains a static method capable of returning either a 062 * {@link DataSource} or a {@link Connection}. 063 * @param methodName The name of the public static method on the aforementioned class that returns the data source 064 * or connection. If this method returns a {@link Connection}, it should return a new connection 065 * every call. 066 * @return the created connection source. 067 */ 068 @PluginFactory 069 public static FactoryMethodConnectionSource createConnectionSource(@PluginAttr("class") final String className, 070 @PluginAttr("method") final String methodName) { 071 if (className == null || className.length() == 0 || methodName == null || methodName.length() == 0) { 072 LOGGER.error("No class name or method name specified for the connection factory method."); 073 return null; 074 } 075 076 final Method method; 077 try { 078 final Class<?> factoryClass = Class.forName(className); 079 method = factoryClass.getMethod(methodName); 080 } catch (final Exception e) { 081 LOGGER.error(e.toString(), e); 082 return null; 083 } 084 085 final Class<?> returnType = method.getReturnType(); 086 String returnTypeString = returnType.getName(); 087 DataSource dataSource; 088 if (returnType == DataSource.class) { 089 try { 090 dataSource = (DataSource) method.invoke(null); 091 returnTypeString += "[" + dataSource + "]"; 092 } catch (final Exception e) { 093 LOGGER.error(e.toString(), e); 094 return null; 095 } 096 } else if (returnType == Connection.class) { 097 dataSource = new DataSource() { 098 @Override 099 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 }