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.Core; 028import org.apache.logging.log4j.core.config.plugins.Plugin; 029import org.apache.logging.log4j.core.config.plugins.PluginAttribute; 030import org.apache.logging.log4j.core.config.plugins.PluginFactory; 031import org.apache.logging.log4j.status.StatusLogger; 032import org.apache.logging.log4j.util.LoaderUtil; 033import org.apache.logging.log4j.util.Strings; 034 035/** 036 * A {@link JdbcAppender} connection source that uses a public static factory method to obtain a {@link Connection} or 037 * {@link DataSource}. 038 */ 039@Plugin(name = "ConnectionFactory", category = Core.CATEGORY_NAME, elementType = "connectionSource", printObject = true) 040public final class FactoryMethodConnectionSource implements ConnectionSource { 041 private static final Logger LOGGER = StatusLogger.getLogger(); 042 043 private final DataSource dataSource; 044 private final String description; 045 046 private FactoryMethodConnectionSource(final DataSource dataSource, final String className, final String methodName, 047 final String returnType) { 048 this.dataSource = dataSource; 049 this.description = "factory{ public static " + returnType + ' ' + className + '.' + methodName + "() }"; 050 } 051 052 @Override 053 public Connection getConnection() throws SQLException { 054 return this.dataSource.getConnection(); 055 } 056 057 @Override 058 public String toString() { 059 return this.description; 060 } 061 062 /** 063 * Factory method for creating a connection source within the plugin manager. 064 * 065 * @param className The name of a public class that contains a static method capable of returning either a 066 * {@link DataSource} or a {@link Connection}. 067 * @param methodName The name of the public static method on the aforementioned class that returns the data source 068 * or connection. If this method returns a {@link Connection}, it should return a new connection 069 * every call. 070 * @return the created connection source. 071 */ 072 @PluginFactory 073 public static FactoryMethodConnectionSource createConnectionSource( 074 @PluginAttribute("class") final String className, 075 @PluginAttribute("method") final String methodName) { 076 if (Strings.isEmpty(className) || Strings.isEmpty(methodName)) { 077 LOGGER.error("No class name or method name specified for the connection factory method."); 078 return null; 079 } 080 081 final Method method; 082 try { 083 final Class<?> factoryClass = LoaderUtil.loadClass(className); 084 method = factoryClass.getMethod(methodName); 085 } catch (final Exception e) { 086 LOGGER.error(e.toString(), e); 087 return null; 088 } 089 090 final Class<?> returnType = method.getReturnType(); 091 String returnTypeString = returnType.getName(); 092 DataSource dataSource; 093 if (returnType == DataSource.class) { 094 try { 095 dataSource = (DataSource) method.invoke(null); 096 returnTypeString += "[" + dataSource + ']'; 097 } catch (final Exception e) { 098 LOGGER.error(e.toString(), e); 099 return null; 100 } 101 } else if (returnType == Connection.class) { 102 dataSource = new DataSource() { 103 @Override 104 public Connection getConnection() throws SQLException { 105 try { 106 return (Connection) method.invoke(null); 107 } catch (final Exception e) { 108 throw new SQLException("Failed to obtain connection from factory method.", e); 109 } 110 } 111 112 @Override 113 public Connection getConnection(final String username, final String password) throws SQLException { 114 throw new UnsupportedOperationException(); 115 } 116 117 @Override 118 public int getLoginTimeout() throws SQLException { 119 throw new UnsupportedOperationException(); 120 } 121 122 @Override 123 public PrintWriter getLogWriter() throws SQLException { 124 throw new UnsupportedOperationException(); 125 } 126 127 @Override 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}