1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package com.xpn.xwiki.store

File DBCPConnectionProvider.java

 

Coverage histogram

../../../../img/srcFileCovDistChart7.png
64% of files have more coverage

Code metrics

30
92
6
1
307
172
31
0.34
15.33
6
5.17

Classes

Class Line # Actions
DBCPConnectionProvider 86 92 0% 31 42
0.67187567.2%
 

Contributing tests

No tests hitting this source file were found.

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package com.xpn.xwiki.store;
21   
22    import java.io.PrintWriter;
23    import java.io.StringWriter;
24    import java.sql.Connection;
25    import java.sql.SQLException;
26    import java.util.Iterator;
27    import java.util.Properties;
28   
29    import org.apache.commons.dbcp2.BasicDataSource;
30    import org.apache.commons.dbcp2.BasicDataSourceFactory;
31    import org.hibernate.HibernateException;
32    import org.hibernate.cfg.Environment;
33    import org.hibernate.connection.ConnectionProvider;
34    import org.hibernate.connection.ConnectionProviderFactory;
35    import org.slf4j.Logger;
36    import org.slf4j.LoggerFactory;
37   
38    /**
39    * <p>
40    * A connection provider that uses an Apache commons DBCP connection pool.
41    * </p>
42    * <p>
43    * To use this connection provider set:<br>
44    * <code>hibernate.connection.provider_class&nbsp;org.hibernate.connection.DBCPConnectionProvider</code>
45    * </p>
46    *
47    * <pre>
48    * Supported Hibernate properties:
49    * hibernate.connection.driver_class
50    * hibernate.connection.url
51    * hibernate.connection.username
52    * hibernate.connection.password
53    * hibernate.connection.isolation
54    * hibernate.connection.autocommit
55    * hibernate.connection.pool_size
56    * hibernate.connection (JDBC driver properties)
57    * </pre>
58    *
59    * <br>
60    * All DBCP properties are also supported by using the hibernate.dbcp prefix. A complete list can be found on the DBCP
61    * configuration page: <a
62    * href="http://jakarta.apache.org/commons/dbcp/configuration.html">http://jakarta.apache.org/commons
63    * /dbcp/configuration.html</a>. <br>
64    *
65    * <pre>
66    * Example:
67    * hibernate.connection.provider_class org.hibernate.connection.DBCPConnectionProvider
68    * hibernate.connection.driver_class org.hsqldb.jdbcDriver
69    * hibernate.connection.username sa
70    * hibernate.connection.password
71    * hibernate.connection.url jdbc:hsqldb:test
72    * hibernate.connection.pool_size 20
73    * hibernate.dbcp.initialSize 10
74    * hibernate.dbcp.maxWait 3000
75    * hibernate.dbcp.validationQuery select 1 from dual
76    * </pre>
77    * <p>
78    * More information about configuring/using DBCP can be found on the <a
79    * href="http://jakarta.apache.org/commons/dbcp/">DBCP website</a>. There you will also find the DBCP wiki, mailing
80    * lists, issue tracking and other support facilities
81    * </p>
82    *
83    * @see org.hibernate.connection.ConnectionProvider
84    * @author Dirk Verbeeck
85    */
 
86    public class DBCPConnectionProvider implements ConnectionProvider
87    {
88    /**
89    * Logger to use to log shutdown information (opposite of initialization).
90    */
91    private static final Logger SHUTDOWN_LOGGER = LoggerFactory.getLogger("org.xwiki.shutdown");
92   
93    private static final Logger LOGGER = LoggerFactory.getLogger(DBCPConnectionProvider.class);
94   
95    private static final String PREFIX = "hibernate.dbcp.";
96   
97    private BasicDataSource ds;
98   
99    // Old Environment property for backward-compatibility (property removed in Hibernate3)
100    private static final String COMPATIBILITY_PS_MAXACTIVE = "ps.maxActive";
101   
102    // Properties removed from DBCP 2.0
103    private static final String COMPATIBILITY_MAXACTIVE = "maxActive";
104    private static final String COMPATIBILITY_MAXWAIT = "maxWait";
105   
106    // Property doesn't exists in Hibernate2
107    private static final String AUTOCOMMIT = "hibernate.connection.autocommit";
108   
 
109  60 toggle @Override
110    public void configure(Properties props) throws HibernateException
111    {
112  60 try {
113  60 LOGGER.debug("Configure DBCPConnectionProvider");
114   
115    // DBCP properties used to create the BasicDataSource
116  60 Properties dbcpProperties = new Properties();
117   
118    // DriverClass & url
119  60 String jdbcDriverClass = props.getProperty(Environment.DRIVER);
120  60 dbcpProperties.put("driverClassName", jdbcDriverClass);
121   
122  60 String jdbcUrl = System.getProperty(Environment.URL);
123  60 if (jdbcUrl == null) {
124  34 jdbcUrl = props.getProperty(Environment.URL);
125    }
126  60 dbcpProperties.put("url", jdbcUrl);
127   
128    // Username / password. Only put username and password if they're not null. This allows
129    // external authentication support (OS authenticated). It'll thus work if the hibernate
130    // config does not specify a username and/or password.
131  60 String username = props.getProperty(Environment.USER);
132  60 if (username != null) {
133  60 dbcpProperties.put("username", username);
134    }
135  60 String password = props.getProperty(Environment.PASS);
136  60 if (password != null) {
137  60 dbcpProperties.put("password", password);
138    }
139   
140    // Isolation level
141  60 String isolationLevel = props.getProperty(Environment.ISOLATION);
142  60 if ((isolationLevel != null) && (isolationLevel.trim().length() > 0)) {
143  0 dbcpProperties.put("defaultTransactionIsolation", isolationLevel);
144    }
145   
146    // Turn off autocommit (unless autocommit property is set)
147    // Note that this property will be overwritten below if the DBCP "defaultAutoCommit" property is defined.
148  60 String autocommit = props.getProperty(AUTOCOMMIT);
149  60 if ((autocommit != null) && (autocommit.trim().length() > 0)) {
150  0 dbcpProperties.put("defaultAutoCommit", autocommit);
151    } else {
152  60 dbcpProperties.put("defaultAutoCommit", String.valueOf(Boolean.FALSE));
153    }
154   
155    // Pool size
156  60 String poolSize = props.getProperty(Environment.POOL_SIZE);
157  60 if ((poolSize != null) && (poolSize.trim().length() > 0) && (Integer.parseInt(poolSize) > 0)) {
158  0 dbcpProperties.put("maxTotal", poolSize);
159    }
160   
161    // Copy all "driver" properties into "connectionProperties"
162  60 Properties driverProps = ConnectionProviderFactory.getConnectionProperties(props);
163  60 if (driverProps.size() > 0) {
164  60 StringBuilder connectionProperties = new StringBuilder();
165  360 for (Iterator iter = driverProps.keySet().iterator(); iter.hasNext();) {
166  300 String key = (String) iter.next();
167  300 String value = driverProps.getProperty(key);
168  300 connectionProperties.append(key).append('=').append(value);
169  300 if (iter.hasNext()) {
170  240 connectionProperties.append(';');
171    }
172    }
173  60 dbcpProperties.put("connectionProperties", connectionProperties.toString());
174    }
175   
176    // Copy all DBCP properties removing the prefix
177  60 for (Object element : props.keySet()) {
178  5672 String key = String.valueOf(element);
179  5672 if (key.startsWith(PREFIX)) {
180  240 String property = key.substring(PREFIX.length());
181  240 String value = props.getProperty(key);
182   
183    // Handle backward compatibility
184  240 switch (property) {
185  0 case COMPATIBILITY_PS_MAXACTIVE:
186  0 dbcpProperties.put("poolPreparedStatements", String.valueOf(Boolean.TRUE));
187  0 dbcpProperties.put("maxOpenPreparedStatements", value);
188  0 break;
189  0 case COMPATIBILITY_MAXACTIVE:
190  0 dbcpProperties.put("maxTotal", value);
191  0 break;
192  0 case COMPATIBILITY_MAXWAIT:
193  0 dbcpProperties.put("maxWaitMillis", value);
194  0 break;
195  240 default:
196  240 dbcpProperties.put(property, value);
197    }
198    }
199    }
200   
201    // Some debug info
202  60 if (LOGGER.isDebugEnabled()) {
203  0 LOGGER.debug("Creating a DBCP BasicDataSource with the following DBCP factory properties:");
204  0 StringWriter sw = new StringWriter();
205  0 dbcpProperties.list(new PrintWriter(sw, true));
206  0 LOGGER.debug(sw.toString());
207    }
208   
209    // Let the factory create the pool
210  60 this.ds = BasicDataSourceFactory.createDataSource(dbcpProperties);
211   
212    // The BasicDataSource has lazy initialization
213    // borrowing a connection will start the DataSource
214    // and make sure it is configured correctly.
215  60 Connection conn = this.ds.getConnection();
216  60 conn.close();
217   
218    // Log pool statistics before continuing.
219  60 logStatistics();
220    } catch (Exception e) {
221  0 String message = "Could not create a DBCP pool. There is an error in the Hibernate configuration file, "
222    + "please review it.";
223  0 LOGGER.error(message, e);
224  0 if (this.ds != null) {
225  0 try {
226  0 this.ds.close();
227    } catch (Exception e2) {
228    // ignore
229    }
230  0 this.ds = null;
231    }
232  0 throw new HibernateException(message, e);
233    }
234  60 LOGGER.debug("Configure DBCPConnectionProvider complete");
235    }
236   
 
237  167927 toggle @Override
238    public Connection getConnection() throws SQLException
239    {
240    // Check if the database has not been already stopped to avoid NPE. This could also possibly happen if the init
241    // has not been already executed and some code calls getConnection().
242  167917 if (this.ds == null) {
243  0 throw new SQLException("Database Connection Pool has not been started or is already stopped!");
244    }
245   
246  167910 Connection conn = null;
247  167921 try {
248  167926 conn = this.ds.getConnection();
249    } finally {
250  167948 logStatistics();
251    }
252  167931 return conn;
253    }
254   
 
255  167940 toggle @Override
256    public void closeConnection(Connection conn) throws SQLException
257    {
258  167944 try {
259  167936 conn.close();
260    } finally {
261  167955 logStatistics();
262    }
263    }
264   
 
265  60 toggle @Override
266    public void close() throws HibernateException
267    {
268  60 SHUTDOWN_LOGGER.debug("Stopping Database Connection Pool...");
269  60 logStatistics();
270  60 try {
271  60 if (this.ds != null) {
272  60 this.ds.close();
273  60 this.ds = null;
274    } else {
275  0 LOGGER.warn("Cannot close Database Connection Pool (not initialized)");
276    }
277    } catch (Exception e) {
278  0 throw new HibernateException("Could not close DBCP pool", e);
279    }
280  60 SHUTDOWN_LOGGER.debug("Database Connection Pool has been stopped");
281    }
282   
283    /**
284    * Does this connection provider support aggressive release of JDBC connections and re-acquistion of those
285    * connections (if need be) later?
286    * <p>
287    * This is used in conjunction with {@link org.hibernate.cfg.Environment#RELEASE_CONNECTIONS} to aggressively
288    * release JDBC connections. However, the configured ConnectionProvider must support re-acquisition of the same
289    * underlying connection for that semantic to work.
290    * <p>
291    * Typically, this is only true in managed environments where a container tracks connections by transaction or
292    * thread.
293    */
 
294  0 toggle @Override
295    public boolean supportsAggressiveRelease()
296    {
297  0 return false;
298    }
299   
 
300  336018 toggle protected void logStatistics()
301    {
302  336009 if (LOGGER.isDebugEnabled()) {
303  0 LOGGER.debug("active: [{}] (max: [{}]), idle: [{}] (max: [{}])", this.ds.getNumActive(),
304    this.ds.getMaxTotal(), this.ds.getNumIdle(), this.ds.getMaxIdle());
305    }
306    }
307    }