    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
    024    import javax.sql.DataSource;
    025
    026    import org.apache.logging.log4j.Logger;
    027    import org.apache.logging.log4j.core.config.plugins.Plugin;
    028    import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
    029    import org.apache.logging.log4j.core.config.plugins.PluginFactory;
    030    import org.apache.logging.log4j.core.util.Loader;
    031    import org.apache.logging.log4j.status.StatusLogger;
    032    import 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)
    039    public 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    }