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; 023import javax.sql.DataSource; 024 025import org.apache.logging.log4j.Logger; 026import org.apache.logging.log4j.core.Core; 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.CATEGORY_NAME, elementType = "connectionSource", printObject = true) 039public final class FactoryMethodConnectionSource extends AbstractConnectionSource { 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 @Override 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}