Skip to content

Commit 83f24c0

Browse files
committed
[LOG4J2-2186] Add a JDBC ConnectionSource that provides pooling through
Apache Commons DBCP 2. Avoid generics compiler differences between Eclipse and Oracle Java.
1 parent 639f093 commit 83f24c0

File tree

14 files changed

+290
-201
lines changed

14 files changed

+290
-201
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache license, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the license for the specific language governing permissions and
15+
* limitations under the license.
16+
*/
17+
18+
package org.apache.logging.log4j.core.appender.db.jdbc;
19+
20+
import org.apache.logging.log4j.core.AbstractLifeCycle;
21+
22+
public abstract class AbstractConnectionSource extends AbstractLifeCycle implements ConnectionSource {
23+
24+
// nothing yet
25+
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache license, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the license for the specific language governing permissions and
15+
* limitations under the license.
16+
*/
17+
package org.apache.logging.log4j.core.appender.db.jdbc;
18+
19+
import java.sql.Connection;
20+
import java.sql.DriverManager;
21+
import java.sql.SQLException;
22+
import java.util.Properties;
23+
24+
import org.apache.logging.log4j.Logger;
25+
import org.apache.logging.log4j.core.config.Property;
26+
import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
27+
import org.apache.logging.log4j.core.config.plugins.PluginElement;
28+
import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
29+
import org.apache.logging.log4j.status.StatusLogger;
30+
31+
/**
32+
* A {@link ConnectionSource} that uses a JDBC connection string, a user name, and a password to call
33+
* {@link DriverManager#getConnection(String, String, String)}.
34+
* <p>
35+
* This plugin does not provide any connection pooling unless it is available through the connection string and driver
36+
* itself. This handy to get you off the ground without having to deal with JNDI.
37+
* </p>
38+
*/
39+
public class AbstractDriverManagerConnectionSource extends AbstractConnectionSource {
40+
41+
/**
42+
* Builds DriverManagerConnectionSource instances.
43+
*
44+
* @param <B>
45+
* This builder type or a subclass.
46+
*/
47+
public static class Builder<B extends Builder<B>> {
48+
49+
@PluginBuilderAttribute
50+
@Required
51+
private String connectionString;
52+
53+
@PluginBuilderAttribute
54+
private String driverClassName;
55+
56+
@PluginBuilderAttribute
57+
private char[] password;
58+
59+
@PluginElement("Properties")
60+
private Property[] properties;
61+
62+
@PluginBuilderAttribute
63+
private char[] userName;
64+
65+
@SuppressWarnings("unchecked")
66+
protected B asBuilder() {
67+
return (B) this;
68+
}
69+
70+
public String getConnectionString() {
71+
return connectionString;
72+
}
73+
74+
public String getDriverClassName() {
75+
return driverClassName;
76+
}
77+
78+
public char[] getPassword() {
79+
return password;
80+
}
81+
82+
public Property[] getProperties() {
83+
return properties;
84+
}
85+
86+
public char[] getUserName() {
87+
return userName;
88+
}
89+
90+
public B setConnectionString(final String connectionString) {
91+
this.connectionString = connectionString;
92+
return asBuilder();
93+
}
94+
95+
public B setDriverClassName(final String driverClassName) {
96+
this.driverClassName = driverClassName;
97+
return asBuilder();
98+
}
99+
100+
public B setPassword(final char[] password) {
101+
this.password = password;
102+
return asBuilder();
103+
}
104+
105+
public B setProperties(final Property[] properties) {
106+
this.properties = properties;
107+
return asBuilder();
108+
}
109+
110+
public B setUserName(final char[] userName) {
111+
this.userName = userName;
112+
return asBuilder();
113+
}
114+
}
115+
116+
private static final Logger LOGGER = StatusLogger.getLogger();
117+
118+
public static Logger getLogger() {
119+
return LOGGER;
120+
}
121+
122+
private final String actualConnectionString;
123+
private final String connectionString;
124+
private final String driverClassName;
125+
private final char[] password;
126+
private final Property[] properties;
127+
private final char[] userName;
128+
129+
public AbstractDriverManagerConnectionSource(final String driverClassName, final String connectionString,
130+
String actualConnectionString, final char[] userName, final char[] password, final Property[] properties) {
131+
super();
132+
this.driverClassName = driverClassName;
133+
this.connectionString = connectionString;
134+
this.actualConnectionString = actualConnectionString;
135+
this.userName = userName;
136+
this.password = password;
137+
this.properties = properties;
138+
}
139+
140+
public String getActualConnectionString() {
141+
return actualConnectionString;
142+
}
143+
144+
@Override
145+
public Connection getConnection() throws SQLException {
146+
loadDriver();
147+
// No, you cannot see the user name and password.
148+
final String actualConnectionString = getActualConnectionString();
149+
LOGGER.debug("Getting connection from DriverManage for '{}'", actualConnectionString);
150+
if (properties != null && properties.length > 0) {
151+
if (userName != null || password != null) {
152+
throw new SQLException("Either set the userName and password, or set the Properties, but not both.");
153+
}
154+
return DriverManager.getConnection(actualConnectionString, toProperties(properties));
155+
}
156+
return DriverManager.getConnection(actualConnectionString, toString(userName), toString(password));
157+
}
158+
159+
public String getConnectionString() {
160+
return connectionString;
161+
}
162+
163+
public String getDriverClassName() {
164+
return driverClassName;
165+
}
166+
167+
public char[] getPassword() {
168+
return password;
169+
}
170+
171+
public Property[] getProperties() {
172+
return properties;
173+
}
174+
175+
public char[] getUserName() {
176+
return userName;
177+
}
178+
179+
protected void loadDriver() throws SQLException {
180+
loadDriver(driverClassName);
181+
}
182+
183+
/**
184+
* Loads a JDBC driver for the given class name
185+
*
186+
* @param className
187+
* the fully-qualified class name for a JDBC Driver.
188+
* @throws SQLException
189+
* thrown when loading the driver throws an exception.
190+
*/
191+
protected void loadDriver(final String className) throws SQLException {
192+
if (className != null) {
193+
// Hack for old JDBC drivers.
194+
try {
195+
Class.forName(className);
196+
} catch (final Exception e) {
197+
throw new SQLException(String.format("The %s could not load the JDBC driver %s: %s",
198+
getClass().getSimpleName(), className, e.toString()), e);
199+
}
200+
}
201+
}
202+
203+
private Properties toProperties(final Property[] properties) {
204+
final Properties props = new Properties();
205+
for (final Property property : properties) {
206+
props.setProperty(property.getName(), property.getValue());
207+
}
208+
return props;
209+
}
210+
211+
@Override
212+
public String toString() {
213+
return this.connectionString;
214+
}
215+
216+
private String toString(final char[] value) {
217+
return value == null ? null : String.valueOf(value);
218+
}
219+
}

log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/ConnectionSource.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@
1919
import java.sql.Connection;
2020
import java.sql.SQLException;
2121

22+
import org.apache.logging.log4j.core.LifeCycle;
23+
2224
/**
2325
* Configuration element for {@link JdbcAppender}. If you want to use the {@link JdbcAppender} but none of the provided
2426
* connection sources meet your needs, you can simply create your own connection source.
2527
*/
26-
public interface ConnectionSource {
28+
public interface ConnectionSource extends LifeCycle{
2729

2830
/**
2931
* This should return a new connection every time it is called.

log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/DataSourceConnectionSource.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
* A {@link JdbcAppender} connection source that uses a {@link DataSource} to connect to the database.
3636
*/
3737
@Plugin(name = "DataSource", category = Core.CATEGORY_NAME, elementType = "connectionSource", printObject = true)
38-
public final class DataSourceConnectionSource implements ConnectionSource {
38+
public final class DataSourceConnectionSource extends AbstractConnectionSource {
3939
private static final Logger LOGGER = StatusLogger.getLogger();
4040

4141
private final DataSource dataSource;

0 commit comments

Comments
 (0)