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.sql.DriverManager; 020import java.sql.SQLException; 021import java.util.concurrent.TimeUnit; 022 023import org.apache.commons.dbcp2.ConnectionFactory; 024import org.apache.commons.dbcp2.DriverManagerConnectionFactory; 025import org.apache.commons.dbcp2.PoolableConnection; 026import org.apache.commons.dbcp2.PoolableConnectionFactory; 027import org.apache.commons.dbcp2.PoolingDriver; 028import org.apache.commons.pool2.ObjectPool; 029import org.apache.commons.pool2.impl.GenericObjectPool; 030import org.apache.logging.log4j.core.Core; 031import org.apache.logging.log4j.core.config.Property; 032import org.apache.logging.log4j.core.config.plugins.Plugin; 033import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; 034 035/** 036 * A {@link ConnectionSource} that uses a JDBC connection string, a user name, and a password to call 037 * {@link DriverManager#getConnection(String, String, String)}. The connections are served from an 038 * <a href="http://commons.apache.org/proper/commons-dbcp/">Apache Commons DBCP</a> pooling driver. 039 */ 040@Plugin(name = "PoolingDriver", category = Core.CATEGORY_NAME, elementType = "connectionSource", printObject = true) 041public final class PoolingDriverConnectionSource extends AbstractDriverManagerConnectionSource { 042 043 /** 044 * Builds PoolingDriverConnectionSource instances. 045 * 046 * @param <B> 047 * This builder type or a subclass. 048 */ 049 public static class Builder<B extends Builder<B>> extends AbstractDriverManagerConnectionSource.Builder<B> 050 implements org.apache.logging.log4j.core.util.Builder<PoolingDriverConnectionSource> { 051 052 public static final String DEFAULT_POOL_NAME = "example"; 053 private String poolName = DEFAULT_POOL_NAME; 054 055 @Override 056 public PoolingDriverConnectionSource build() { 057 try { 058 return new PoolingDriverConnectionSource(getDriverClassName(), getConnectionString(), getUserName(), 059 getPassword(), getProperties(), poolName); 060 } catch (final SQLException e) { 061 getLogger().error("Exception constructing {} to '{}'", getClass(), getConnectionString(), e); 062 return null; 063 } 064 } 065 066 public B setPoolName(final String poolName) { 067 this.poolName = poolName; 068 return asBuilder(); 069 } 070 } 071 072 public static final String URL_PREFIX = "jdbc:apache:commons:dbcp:"; 073 074 // This method is not named newBuilder() to make the compiler happy. 075 @PluginBuilderFactory 076 public static <B extends Builder<B>> B newPoolingDriverConnectionSourceBuilder() { 077 return new Builder<B>().asBuilder(); 078 } 079 080 private final String poolingDriverClassName = "org.apache.commons.dbcp2.PoolingDriver"; 081 082 private final String poolName; 083 084 public PoolingDriverConnectionSource(final String driverClassName, final String connectionString, 085 final char[] userName, final char[] password, final Property[] properties, final String poolName) 086 throws SQLException { 087 super(driverClassName, connectionString, URL_PREFIX + poolName, userName, password, properties); 088 this.poolName = poolName; 089 setupDriver(connectionString); 090 } 091 092 @Override 093 public String getActualConnectionString() { 094 // TODO Auto-generated method stub 095 return super.getActualConnectionString(); 096 } 097 098 private PoolingDriver getPoolingDriver() throws SQLException { 099 final PoolingDriver driver = (PoolingDriver) DriverManager.getDriver(URL_PREFIX); 100 if (driver == null) { 101 getLogger().error("No JDBC driver for '{}'", URL_PREFIX); 102 } 103 return driver; 104 } 105 106 private void setupDriver(final String connectionString) throws SQLException { 107 // 108 // First, we'll create a ConnectionFactory that the 109 // pool will use to create Connections. 110 // We'll use the DriverManagerConnectionFactory, 111 // using the connect string passed in the command line 112 // arguments. 113 // 114 final ConnectionFactory connectionFactory = new DriverManagerConnectionFactory(connectionString, null); 115 116 // 117 // Next, we'll create the PoolableConnectionFactory, which wraps 118 // the "real" Connections created by the ConnectionFactory with 119 // the classes that implement the pooling functionality. 120 // 121 final PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(connectionFactory, 122 null); 123 124 // 125 // Now we'll need a ObjectPool that serves as the 126 // actual pool of connections. 127 // 128 // We'll use a GenericObjectPool instance, although 129 // any ObjectPool implementation will suffice. 130 // 131 @SuppressWarnings("resource") 132 // This GenericObjectPool will be closed on shutown 133 final ObjectPool<PoolableConnection> connectionPool = new GenericObjectPool<>(poolableConnectionFactory); 134 135 // Set the factory's pool property to the owning pool 136 poolableConnectionFactory.setPool(connectionPool); 137 138 loadDriver(poolingDriverClassName); 139 final PoolingDriver driver = getPoolingDriver(); 140 if (driver != null) { 141 getLogger().debug("Registering DBCP pool '{}'", poolName); 142 driver.registerPool(poolName, connectionPool); 143 } 144 // 145 // Now we can just use the connect string "jdbc:apache:commons:dbcp:example" 146 // to access our pool of Connections. 147 // 148 } 149 150 @Override 151 public boolean stop(long timeout, TimeUnit timeUnit) { 152 try { 153 final PoolingDriver driver = getPoolingDriver(); 154 if (driver != null) { 155 getLogger().debug("Closing DBCP pool '{}'", poolName); 156 driver.closePool(poolName); 157 } 158 return true; 159 } catch (Exception e) { 160 getLogger().error("Exception stopping connection source for '{}' → '{}'", getConnectionString(), 161 getActualConnectionString(), e); 162 return false; 163 } 164 } 165}