2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2016 - 2017 AT&T
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
22 * Licensed to the Apache Software Foundation (ASF) under one or more
23 * contributor license agreements. See the NOTICE file distributed with
24 * this work for additional information regarding copyright ownership.
25 * The ASF licenses this file to You under the Apache License, Version 2.0
26 * (the "License"); you may not use this file except in compliance with
27 * the License. You may obtain a copy of the License at
29 * http://www.apache.org/licenses/LICENSE-2.0
31 * Unless required by applicable law or agreed to in writing, software
32 * distributed under the License is distributed on an "AS IS" BASIS,
33 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
34 * See the License for the specific language governing permissions and
35 * limitations under the License.
37 package org.apache.tomcat.jdbc.pool;
40 import java.sql.Connection;
41 import java.util.Hashtable;
42 import java.util.Properties;
44 import javax.management.ObjectName;
45 import javax.naming.Context;
46 import javax.naming.InitialContext;
47 import javax.naming.Name;
48 import javax.naming.NamingException;
49 import javax.naming.RefAddr;
50 import javax.naming.Reference;
51 import javax.naming.spi.ObjectFactory;
52 import javax.sql.DataSource;
54 import org.apache.juli.logging.Log;
55 import org.apache.juli.logging.LogFactory;
58 * <p>JNDI object factory that creates an instance of
59 * <code>BasicDataSource</code> that has been configured based on the
60 * <code>RefAddr</code> values of the specified <code>Reference</code>,
61 * which must match the names and data types of the
62 * <code>BasicDataSource</code> bean properties.</p>
64 * Properties available for configuration:<br>
65 * <a href="http://commons.apache.org/dbcp/configuration.html">Commons DBCP properties</a><br>
67 * <li>initSQL - A query that gets executed once, right after the connection is established.</li>
68 * <li>testOnConnect - run validationQuery after connection has been established.</li>
69 * <li>validationInterval - avoid excess validation, only run validation at most at this frequency - time in milliseconds.</li>
70 * <li>jdbcInterceptors - a semicolon separated list of classnames extending {@link JdbcInterceptor} class.</li>
71 * <li>jmxEnabled - true of false, whether to register the pool with JMX.</li>
72 * <li>fairQueue - true of false, whether the pool should sacrifice a little bit of performance for true fairness.</li>
74 * @author Craig R. McClanahan
75 * @author Dirk Verbeeck
77 public class DataSourceFactory implements ObjectFactory {
78 private static final Log log = LogFactory.getLog(DataSourceFactory.class);
80 protected static final String PROP_DEFAULTAUTOCOMMIT = "defaultAutoCommit";
81 protected static final String PROP_DEFAULTREADONLY = "defaultReadOnly";
82 protected static final String PROP_DEFAULTTRANSACTIONISOLATION = "defaultTransactionIsolation";
83 protected static final String PROP_DEFAULTCATALOG = "defaultCatalog";
85 protected static final String PROP_DRIVERCLASSNAME = "driverClassName";
86 protected static final String PROP_PASSWORD = "password";
87 protected static final String PROP_URL = "url";
88 protected static final String PROP_USERNAME = "username";
90 protected static final String PROP_MAXACTIVE = "maxActive";
91 protected static final String PROP_MAXIDLE = "maxIdle";
92 protected static final String PROP_MINIDLE = "minIdle";
93 protected static final String PROP_INITIALSIZE = "initialSize";
94 protected static final String PROP_MAXWAIT = "maxWait";
95 protected static final String PROP_MAXAGE = "maxAge";
97 protected static final String PROP_TESTONBORROW = "testOnBorrow";
98 protected static final String PROP_TESTONRETURN = "testOnReturn";
99 protected static final String PROP_TESTWHILEIDLE = "testWhileIdle";
100 protected static final String PROP_TESTONCONNECT = "testOnConnect";
101 protected static final String PROP_VALIDATIONQUERY = "validationQuery";
102 protected static final String PROP_VALIDATIONQUERY_TIMEOUT = "validationQueryTimeout";
103 protected static final String PROP_VALIDATOR_CLASS_NAME = "validatorClassName";
105 protected static final String PROP_NUMTESTSPEREVICTIONRUN = "numTestsPerEvictionRun";
106 protected static final String PROP_TIMEBETWEENEVICTIONRUNSMILLIS = "timeBetweenEvictionRunsMillis";
107 protected static final String PROP_MINEVICTABLEIDLETIMEMILLIS = "minEvictableIdleTimeMillis";
109 protected static final String PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED = "accessToUnderlyingConnectionAllowed";
111 protected static final String PROP_REMOVEABANDONED = "removeAbandoned";
112 protected static final String PROP_REMOVEABANDONEDTIMEOUT = "removeAbandonedTimeout";
113 protected static final String PROP_LOGABANDONED = "logAbandoned";
114 protected static final String PROP_ABANDONWHENPERCENTAGEFULL = "abandonWhenPercentageFull";
116 protected static final String PROP_POOLPREPAREDSTATEMENTS = "poolPreparedStatements";
117 protected static final String PROP_MAXOPENPREPAREDSTATEMENTS = "maxOpenPreparedStatements";
118 protected static final String PROP_CONNECTIONPROPERTIES = "connectionProperties";
120 protected static final String PROP_INITSQL = "initSQL";
121 protected static final String PROP_INTERCEPTORS = "jdbcInterceptors";
122 protected static final String PROP_VALIDATIONINTERVAL = "validationInterval";
123 protected static final String PROP_JMX_ENABLED = "jmxEnabled";
124 protected static final String PROP_FAIR_QUEUE = "fairQueue";
126 protected static final String PROP_USE_EQUALS = "useEquals";
127 protected static final String PROP_USE_CON_LOCK = "useLock";
129 protected static final String PROP_DATASOURCE= "dataSource";
130 protected static final String PROP_DATASOURCE_JNDI = "dataSourceJNDI";
132 protected static final String PROP_SUSPECT_TIMEOUT = "suspectTimeout";
134 protected static final String PROP_ALTERNATE_USERNAME_ALLOWED = "alternateUsernameAllowed";
136 protected static final String PROP_COMMITONRETURN = "commitOnReturn";
137 protected static final String PROP_ROLLBACKONRETURN = "rollbackOnReturn";
139 protected static final String PROP_USEDISPOSABLECONNECTIONFACADE = "useDisposableConnectionFacade";
141 protected static final String PROP_LOGVALIDATIONERRORS = "logValidationErrors";
143 protected static final String PROP_PROPAGATEINTERRUPTSTATE = "propagateInterruptState";
145 protected static final String PROP_IGNOREEXCEPTIONONPRELOAD = "ignoreExceptionOnPreLoad";
147 public static final int UNKNOWN_TRANSACTIONISOLATION = -1;
149 public static final String OBJECT_NAME = "object_name";
152 protected static final String[] ALL_PROPERTIES = {
153 PROP_DEFAULTAUTOCOMMIT,
154 PROP_DEFAULTREADONLY,
155 PROP_DEFAULTTRANSACTIONISOLATION,
157 PROP_DRIVERCLASSNAME,
165 PROP_TIMEBETWEENEVICTIONRUNSMILLIS,
166 PROP_NUMTESTSPEREVICTIONRUN,
167 PROP_MINEVICTABLEIDLETIMEMILLIS,
173 PROP_VALIDATIONQUERY,
174 PROP_VALIDATIONQUERY_TIMEOUT,
175 PROP_VALIDATOR_CLASS_NAME,
176 PROP_VALIDATIONINTERVAL,
177 PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED,
178 PROP_REMOVEABANDONED,
179 PROP_REMOVEABANDONEDTIMEOUT,
181 PROP_POOLPREPAREDSTATEMENTS,
182 PROP_MAXOPENPREPAREDSTATEMENTS,
183 PROP_CONNECTIONPROPERTIES,
190 PROP_ABANDONWHENPERCENTAGEFULL,
194 PROP_DATASOURCE_JNDI,
195 PROP_SUSPECT_TIMEOUT,
196 PROP_ALTERNATE_USERNAME_ALLOWED,
198 PROP_ROLLBACKONRETURN,
199 PROP_USEDISPOSABLECONNECTIONFACADE,
200 PROP_LOGVALIDATIONERRORS,
201 PROP_PROPAGATEINTERRUPTSTATE,
202 PROP_IGNOREEXCEPTIONONPRELOAD
205 // -------------------------------------------------- ObjectFactory Methods
208 * <p>Create and return a new <code>BasicDataSource</code> instance. If no
209 * instance can be created, return <code>null</code> instead.</p>
211 * @param obj The possibly null object containing location or
212 * reference information that can be used in creating an object
213 * @param name The name of this object relative to <code>nameCtx</code>
214 * @param nameCtx The context relative to which the <code>name</code>
215 * parameter is specified, or <code>null</code> if <code>name</code>
216 * is relative to the default initial context
217 * @param environment The possibly null environment that is used in
218 * creating this object
220 * @exception Exception if an exception occurs creating the instance
223 public Object getObjectInstance(Object obj, Name name, Context nameCtx,
224 Hashtable<?,?> environment) throws Exception {
226 // We only know how to deal with <code>javax.naming.Reference</code>s
227 // that specify a class name of "javax.sql.DataSource"
228 if ((obj == null) || !(obj instanceof Reference)) {
231 Reference ref = (Reference) obj;
234 if ("javax.sql.DataSource".equals(ref.getClassName())) {
237 if ("javax.sql.XADataSource".equals(ref.getClassName())) {
241 if (org.apache.tomcat.jdbc.pool.DataSource.class.getName().equals(ref.getClassName())) {
246 log.warn(ref.getClassName()+" is not a valid class name/type for this JNDI factory.");
251 Properties properties = new Properties();
252 for (int i = 0; i < ALL_PROPERTIES.length; i++) {
253 String propertyName = ALL_PROPERTIES[i];
254 RefAddr ra = ref.get(propertyName);
256 String propertyValue = ra.getContent().toString();
257 properties.setProperty(propertyName, propertyValue);
261 return createDataSource(properties,nameCtx,XA);
264 public static PoolConfiguration parsePoolProperties(Properties properties) {
265 PoolConfiguration poolProperties = new PoolProperties();
268 value = properties.getProperty(PROP_DEFAULTAUTOCOMMIT);
270 poolProperties.setDefaultAutoCommit(Boolean.valueOf(value));
273 value = properties.getProperty(PROP_DEFAULTREADONLY);
275 poolProperties.setDefaultReadOnly(Boolean.valueOf(value));
278 value = properties.getProperty(PROP_DEFAULTTRANSACTIONISOLATION);
280 int level = UNKNOWN_TRANSACTIONISOLATION;
281 if ("NONE".equalsIgnoreCase(value)) {
282 level = Connection.TRANSACTION_NONE;
283 } else if ("READ_COMMITTED".equalsIgnoreCase(value)) {
284 level = Connection.TRANSACTION_READ_COMMITTED;
285 } else if ("READ_UNCOMMITTED".equalsIgnoreCase(value)) {
286 level = Connection.TRANSACTION_READ_UNCOMMITTED;
287 } else if ("REPEATABLE_READ".equalsIgnoreCase(value)) {
288 level = Connection.TRANSACTION_REPEATABLE_READ;
289 } else if ("SERIALIZABLE".equalsIgnoreCase(value)) {
290 level = Connection.TRANSACTION_SERIALIZABLE;
293 level = Integer.parseInt(value);
294 } catch (NumberFormatException e) {
295 System.err.println("Could not parse defaultTransactionIsolation: " + value);
296 System.err.println("WARNING: defaultTransactionIsolation not set");
297 System.err.println("using default value of database driver");
298 level = UNKNOWN_TRANSACTIONISOLATION;
301 poolProperties.setDefaultTransactionIsolation(level);
304 value = properties.getProperty(PROP_DEFAULTCATALOG);
306 poolProperties.setDefaultCatalog(value);
309 value = properties.getProperty(PROP_DRIVERCLASSNAME);
311 poolProperties.setDriverClassName(value);
314 value = properties.getProperty(PROP_MAXACTIVE);
316 poolProperties.setMaxActive(Integer.parseInt(value));
319 value = properties.getProperty(PROP_MAXIDLE);
321 poolProperties.setMaxIdle(Integer.parseInt(value));
324 value = properties.getProperty(PROP_MINIDLE);
326 poolProperties.setMinIdle(Integer.parseInt(value));
329 value = properties.getProperty(PROP_INITIALSIZE);
331 poolProperties.setInitialSize(Integer.parseInt(value));
334 value = properties.getProperty(PROP_MAXWAIT);
336 poolProperties.setMaxWait(Integer.parseInt(value));
339 value = properties.getProperty(PROP_TESTONBORROW);
341 poolProperties.setTestOnBorrow(Boolean.parseBoolean(value));
344 value = properties.getProperty(PROP_TESTONRETURN);
346 poolProperties.setTestOnReturn(Boolean.parseBoolean(value));
349 value = properties.getProperty(PROP_TESTONCONNECT);
351 poolProperties.setTestOnConnect(Boolean.parseBoolean(value));
354 value = properties.getProperty(PROP_TIMEBETWEENEVICTIONRUNSMILLIS);
356 poolProperties.setTimeBetweenEvictionRunsMillis(Integer.parseInt(value));
359 value = properties.getProperty(PROP_NUMTESTSPEREVICTIONRUN);
361 poolProperties.setNumTestsPerEvictionRun(Integer.parseInt(value));
364 value = properties.getProperty(PROP_MINEVICTABLEIDLETIMEMILLIS);
366 poolProperties.setMinEvictableIdleTimeMillis(Integer.parseInt(value));
369 value = properties.getProperty(PROP_TESTWHILEIDLE);
371 poolProperties.setTestWhileIdle(Boolean.parseBoolean(value));
374 value = properties.getProperty(PROP_PASSWORD);
376 poolProperties.setPassword(value);
379 value = properties.getProperty(PROP_URL);
381 poolProperties.setUrl(value);
384 value = properties.getProperty(PROP_USERNAME);
386 poolProperties.setUsername(value);
389 value = properties.getProperty(PROP_VALIDATIONQUERY);
391 poolProperties.setValidationQuery(value);
394 value = properties.getProperty(PROP_VALIDATIONQUERY_TIMEOUT);
396 poolProperties.setValidationQueryTimeout(Integer.parseInt(value));
399 value = properties.getProperty(PROP_VALIDATOR_CLASS_NAME);
401 poolProperties.setValidatorClassName(value);
404 value = properties.getProperty(PROP_VALIDATIONINTERVAL);
406 poolProperties.setValidationInterval(Long.parseLong(value));
409 value = properties.getProperty(PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED);
411 poolProperties.setAccessToUnderlyingConnectionAllowed(Boolean.parseBoolean(value));
414 value = properties.getProperty(PROP_REMOVEABANDONED);
416 poolProperties.setRemoveAbandoned(Boolean.parseBoolean(value));
419 value = properties.getProperty(PROP_REMOVEABANDONEDTIMEOUT);
421 poolProperties.setRemoveAbandonedTimeout(Integer.parseInt(value));
424 value = properties.getProperty(PROP_LOGABANDONED);
426 poolProperties.setLogAbandoned(Boolean.parseBoolean(value));
429 value = properties.getProperty(PROP_POOLPREPAREDSTATEMENTS);
431 log.warn(PROP_POOLPREPAREDSTATEMENTS + " is not a valid setting, it will have no effect.");
434 value = properties.getProperty(PROP_MAXOPENPREPAREDSTATEMENTS);
436 log.warn(PROP_MAXOPENPREPAREDSTATEMENTS + " is not a valid setting, it will have no effect.");
439 value = properties.getProperty(PROP_CONNECTIONPROPERTIES);
441 Properties p = getProperties(value);
442 poolProperties.setDbProperties(p);
444 poolProperties.setDbProperties(new Properties());
447 if (poolProperties.getUsername()!=null) {
448 poolProperties.getDbProperties().setProperty("user",poolProperties.getUsername());
450 if (poolProperties.getPassword()!=null) {
451 poolProperties.getDbProperties().setProperty("password",poolProperties.getPassword());
454 value = properties.getProperty(PROP_INITSQL);
456 poolProperties.setInitSQL(value);
459 value = properties.getProperty(PROP_INTERCEPTORS);
461 poolProperties.setJdbcInterceptors(value);
464 value = properties.getProperty(PROP_JMX_ENABLED);
466 poolProperties.setJmxEnabled(Boolean.parseBoolean(value));
469 value = properties.getProperty(PROP_FAIR_QUEUE);
471 poolProperties.setFairQueue(Boolean.parseBoolean(value));
474 value = properties.getProperty(PROP_USE_EQUALS);
476 poolProperties.setUseEquals(Boolean.parseBoolean(value));
479 value = properties.getProperty(OBJECT_NAME);
481 poolProperties.setName(ObjectName.quote(value));
484 value = properties.getProperty(PROP_ABANDONWHENPERCENTAGEFULL);
486 poolProperties.setAbandonWhenPercentageFull(Integer.parseInt(value));
489 value = properties.getProperty(PROP_MAXAGE);
491 poolProperties.setMaxAge(Long.parseLong(value));
494 value = properties.getProperty(PROP_USE_CON_LOCK);
496 poolProperties.setUseLock(Boolean.parseBoolean(value));
499 value = properties.getProperty(PROP_DATASOURCE);
501 //this should never happen
502 throw new IllegalArgumentException("Can't set dataSource property as a string, this must be a javax.sql.DataSource object.");
506 value = properties.getProperty(PROP_DATASOURCE_JNDI);
508 poolProperties.setDataSourceJNDI(value);
511 value = properties.getProperty(PROP_SUSPECT_TIMEOUT);
513 poolProperties.setSuspectTimeout(Integer.parseInt(value));
516 value = properties.getProperty(PROP_ALTERNATE_USERNAME_ALLOWED);
518 poolProperties.setAlternateUsernameAllowed(Boolean.parseBoolean(value));
521 value = properties.getProperty(PROP_COMMITONRETURN);
523 poolProperties.setCommitOnReturn(Boolean.parseBoolean(value));
526 value = properties.getProperty(PROP_ROLLBACKONRETURN);
528 poolProperties.setRollbackOnReturn(Boolean.parseBoolean(value));
531 value = properties.getProperty(PROP_USEDISPOSABLECONNECTIONFACADE);
533 poolProperties.setUseDisposableConnectionFacade(Boolean.parseBoolean(value));
536 value = properties.getProperty(PROP_LOGVALIDATIONERRORS);
538 poolProperties.setLogValidationErrors(Boolean.parseBoolean(value));
541 value = properties.getProperty(PROP_PROPAGATEINTERRUPTSTATE);
543 poolProperties.setPropagateInterruptState(Boolean.parseBoolean(value));
546 value = properties.getProperty(PROP_IGNOREEXCEPTIONONPRELOAD);
548 poolProperties.setIgnoreExceptionOnPreLoad(Boolean.parseBoolean(value));
551 return poolProperties;
555 * Creates and configures a {@link DataSource} instance based on the
558 * @param properties the datasource configuration properties
559 * @return the datasource
560 * @throws Exception if an error occurs creating the data source
562 public DataSource createDataSource(Properties properties) throws Exception {
563 return createDataSource(properties,null,false);
565 public DataSource createDataSource(Properties properties,Context context, boolean XA) throws Exception {
566 PoolConfiguration poolProperties = DataSourceFactory.parsePoolProperties(properties);
567 if (poolProperties.getDataSourceJNDI()!=null && poolProperties.getDataSource()==null) {
568 performJNDILookup(context, poolProperties);
570 org.apache.tomcat.jdbc.pool.DataSource dataSource = XA?
571 new org.apache.tomcat.jdbc.pool.XADataSource(poolProperties) :
572 new org.apache.tomcat.jdbc.pool.DataSource(poolProperties);
573 //initialise the pool itself
574 dataSource.createPool();
575 // Return the configured DataSource instance
579 public void performJNDILookup(Context context, PoolConfiguration poolProperties) {
580 Object jndiDS = null;
583 jndiDS = context.lookup(poolProperties.getDataSourceJNDI());
585 log.warn("dataSourceJNDI property is configured, but local JNDI context is null.");
587 } catch (NamingException e) {
588 log.debug("The name \""+poolProperties.getDataSourceJNDI()+"\" cannot be found in the local context.");
592 context = new InitialContext();
593 jndiDS = context.lookup(poolProperties.getDataSourceJNDI());
594 } catch (NamingException e) {
595 log.warn("The name \""+poolProperties.getDataSourceJNDI()+"\" cannot be found in the InitialContext.");
599 poolProperties.setDataSource(jndiDS);
604 * Parse properties from the string. Format of the string must be [propertyName=property;]*.
605 * @param propText The properties string
606 * @return the properties
608 protected static Properties getProperties(String propText) {
609 return PoolProperties.getProperties(propText,null);