View Javadoc
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          protected String connectionString;
52  
53          @PluginBuilderAttribute
54          protected String driverClassName;
55  
56          @PluginBuilderAttribute
57          protected char[] password;
58  
59          @PluginElement("Properties")
60          protected Property[] properties;
61  
62          @PluginBuilderAttribute
63          protected 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             final 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     @SuppressWarnings("resource") // The JDBC Connection is freed when the connection source is stopped.
145     @Override
146     public Connection getConnection() throws SQLException {
147         loadDriver();
148         final String actualConnectionString = getActualConnectionString();
149         LOGGER.debug("{} getting connection for '{}'", getClass().getSimpleName(), actualConnectionString);
150         Connection connection;
151         if (properties != null && properties.length > 0) {
152             if (userName != null || password != null) {
153                 throw new SQLException("Either set the userName and password, or set the Properties, but not both.");
154             }
155             connection = DriverManager.getConnection(actualConnectionString, toProperties(properties));
156         } else {
157             connection = DriverManager.getConnection(actualConnectionString, toString(userName), toString(password));
158         }
159         LOGGER.debug("{} acquired connection for '{}': {} ({}@{})", getClass().getSimpleName(), actualConnectionString,
160                 connection, connection.getClass().getName(), Integer.toHexString(connection.hashCode()));
161         return connection;
162     }
163 
164     public String getConnectionString() {
165         return connectionString;
166     }
167 
168     public String getDriverClassName() {
169         return driverClassName;
170     }
171 
172     public char[] getPassword() {
173         return password;
174     }
175 
176     public Property[] getProperties() {
177         return properties;
178     }
179 
180     public char[] getUserName() {
181         return userName;
182     }
183 
184     protected void loadDriver() throws SQLException {
185         loadDriver(driverClassName);
186     }
187 
188     /**
189      * Loads a JDBC driver for the given class name
190      *
191      * @param className
192      *            the fully-qualified class name for a JDBC Driver.
193      * @throws SQLException
194      *             thrown when loading the driver throws an exception.
195      */
196     protected void loadDriver(final String className) throws SQLException {
197         if (className != null) {
198             // Hack for old JDBC drivers.
199             LOGGER.debug("Loading driver class {}", className);
200             try {
201                 Class.forName(className);
202             } catch (final Exception e) {
203                 throw new SQLException(String.format("The %s could not load the JDBC driver %s: %s",
204                         getClass().getSimpleName(), className, e.toString()), e);
205             }
206         }
207     }
208 
209     protected Properties toProperties(final Property[] properties) {
210         final Properties props = new Properties();
211         for (final Property property : properties) {
212             props.setProperty(property.getName(), property.getValue());
213         }
214         return props;
215     }
216 
217     @Override
218     public String toString() {
219         return this.connectionString;
220     }
221 
222     protected String toString(final char[] value) {
223         return value == null ? null : String.valueOf(value);
224     }
225 }