001
014
015 package com.liferay.portal.dao.jdbc;
016
017 import com.liferay.portal.dao.jdbc.pool.metrics.C3P0ConnectionPoolMetrics;
018 import com.liferay.portal.dao.jdbc.pool.metrics.DBCPConnectionPoolMetrics;
019 import com.liferay.portal.dao.jdbc.pool.metrics.HikariConnectionPoolMetrics;
020 import com.liferay.portal.dao.jdbc.pool.metrics.TomcatConnectionPoolMetrics;
021 import com.liferay.portal.dao.jdbc.util.DataSourceWrapper;
022 import com.liferay.portal.dao.jdbc.util.RetryDataSourceWrapper;
023 import com.liferay.portal.kernel.configuration.Filter;
024 import com.liferay.portal.kernel.dao.db.DBManagerUtil;
025 import com.liferay.portal.kernel.dao.db.DBType;
026 import com.liferay.portal.kernel.dao.jdbc.DataSourceFactory;
027 import com.liferay.portal.kernel.dao.jdbc.pool.metrics.ConnectionPoolMetrics;
028 import com.liferay.portal.kernel.exception.SystemException;
029 import com.liferay.portal.kernel.jndi.JNDIUtil;
030 import com.liferay.portal.kernel.log.Log;
031 import com.liferay.portal.kernel.log.LogFactoryUtil;
032 import com.liferay.portal.kernel.security.pacl.DoPrivileged;
033 import com.liferay.portal.kernel.util.ClassLoaderUtil;
034 import com.liferay.portal.kernel.util.PropertiesUtil;
035 import com.liferay.portal.kernel.util.PropsKeys;
036 import com.liferay.portal.kernel.util.ServerDetector;
037 import com.liferay.portal.kernel.util.SortedProperties;
038 import com.liferay.portal.kernel.util.StringUtil;
039 import com.liferay.portal.kernel.util.Validator;
040 import com.liferay.portal.spring.hibernate.DialectDetector;
041 import com.liferay.portal.util.JarUtil;
042 import com.liferay.portal.util.PropsUtil;
043 import com.liferay.portal.util.PropsValues;
044 import com.liferay.registry.Registry;
045 import com.liferay.registry.RegistryUtil;
046 import com.liferay.registry.ServiceReference;
047 import com.liferay.registry.ServiceTracker;
048 import com.liferay.registry.ServiceTrackerCustomizer;
049
050 import com.mchange.v2.c3p0.ComboPooledDataSource;
051
052 import java.net.URL;
053 import java.net.URLClassLoader;
054
055 import java.util.Enumeration;
056 import java.util.Map;
057 import java.util.Properties;
058
059 import javax.management.MBeanServer;
060 import javax.management.MalformedObjectNameException;
061 import javax.management.ObjectName;
062
063 import javax.naming.Context;
064 import javax.naming.InitialContext;
065
066 import javax.sql.DataSource;
067
068 import jodd.bean.BeanUtil;
069
070 import org.apache.commons.dbcp.BasicDataSource;
071 import org.apache.commons.dbcp.BasicDataSourceFactory;
072 import org.apache.tomcat.jdbc.pool.PoolProperties;
073 import org.apache.tomcat.jdbc.pool.jmx.ConnectionPool;
074
075
079 @DoPrivileged
080 public class DataSourceFactoryImpl implements DataSourceFactory {
081
082 @Override
083 public void destroyDataSource(DataSource dataSource) throws Exception {
084 while (dataSource instanceof DataSourceWrapper) {
085 DataSourceWrapper dataSourceWrapper = (DataSourceWrapper)dataSource;
086
087 dataSource = dataSourceWrapper.getWrappedDataSource();
088 }
089
090 if (dataSource instanceof ComboPooledDataSource) {
091 ComboPooledDataSource comboPooledDataSource =
092 (ComboPooledDataSource)dataSource;
093
094 comboPooledDataSource.close();
095 }
096 else if (dataSource instanceof org.apache.tomcat.jdbc.pool.DataSource) {
097 org.apache.tomcat.jdbc.pool.DataSource tomcatDataSource =
098 (org.apache.tomcat.jdbc.pool.DataSource)dataSource;
099
100 if (_serviceTracker != null) {
101 _serviceTracker.close();
102 }
103
104 tomcatDataSource.close();
105 }
106 }
107
108 @Override
109 public DataSource initDataSource(Properties properties) throws Exception {
110 Properties defaultProperties = PropsUtil.getProperties(
111 "jdbc.default.", true);
112
113 PropertiesUtil.merge(defaultProperties, properties);
114
115 properties = defaultProperties;
116
117 String jndiName = properties.getProperty("jndi.name");
118
119 if (Validator.isNotNull(jndiName)) {
120 try {
121 Properties jndiEnvironmentProperties = PropsUtil.getProperties(
122 PropsKeys.JNDI_ENVIRONMENT, true);
123
124 Context context = new InitialContext(jndiEnvironmentProperties);
125
126 return (DataSource)JNDIUtil.lookup(context, jndiName);
127 }
128 catch (Exception e) {
129 _log.error("Unable to lookup " + jndiName, e);
130 }
131 }
132
133 if (_log.isDebugEnabled()) {
134 _log.debug("Data source properties:\n");
135
136 SortedProperties sortedProperties = new SortedProperties(
137 properties);
138
139 _log.debug(PropertiesUtil.toString(sortedProperties));
140 }
141
142 testDatabaseClass(properties);
143
144 DataSource dataSource = null;
145
146 String liferayPoolProvider =
147 PropsValues.JDBC_DEFAULT_LIFERAY_POOL_PROVIDER;
148
149 if (StringUtil.equalsIgnoreCase(liferayPoolProvider, "c3p0") ||
150 StringUtil.equalsIgnoreCase(liferayPoolProvider, "c3po")) {
151
152 if (_log.isDebugEnabled()) {
153 _log.debug("Initializing C3P0 data source");
154 }
155
156 dataSource = initDataSourceC3PO(properties);
157 }
158 else if (StringUtil.equalsIgnoreCase(liferayPoolProvider, "dbcp")) {
159 if (_log.isDebugEnabled()) {
160 _log.debug("Initializing DBCP data source");
161 }
162
163 dataSource = initDataSourceDBCP(properties);
164 }
165 else if (StringUtil.equalsIgnoreCase(liferayPoolProvider, "hikaricp")) {
166 if (_log.isDebugEnabled()) {
167 _log.debug("Initializing HikariCP data source");
168 }
169
170 dataSource = initDataSourceHikariCP(properties);
171 }
172 else {
173 if (_log.isDebugEnabled()) {
174 _log.debug("Initializing Tomcat data source");
175 }
176
177 dataSource = initDataSourceTomcat(properties);
178 }
179
180 if (_log.isDebugEnabled()) {
181 _log.debug("Created data source " + dataSource.getClass());
182 }
183
184 if (PropsValues.RETRY_DATA_SOURCE_MAX_RETRIES > 0) {
185 DBType dbType = DBManagerUtil.getDBType(
186 DialectDetector.getDialect(dataSource));
187
188 if (dbType == DBType.SYBASE) {
189 dataSource = new RetryDataSourceWrapper(dataSource);
190 }
191 }
192
193 return _pacl.getDataSource(dataSource);
194 }
195
196 @Override
197 public DataSource initDataSource(
198 String driverClassName, String url, String userName,
199 String password, String jndiName)
200 throws Exception {
201
202 Properties properties = new Properties();
203
204 properties.setProperty("driverClassName", driverClassName);
205 properties.setProperty("url", url);
206 properties.setProperty("username", userName);
207 properties.setProperty("password", password);
208 properties.setProperty("jndi.name", jndiName);
209
210 return initDataSource(properties);
211 }
212
213 public interface PACL {
214
215 public DataSource getDataSource(DataSource dataSource);
216
217 }
218
219 protected DataSource initDataSourceC3PO(Properties properties)
220 throws Exception {
221
222 ComboPooledDataSource comboPooledDataSource =
223 new ComboPooledDataSource();
224
225 String identityToken = StringUtil.randomString();
226
227 comboPooledDataSource.setIdentityToken(identityToken);
228
229 Enumeration<String> enu =
230 (Enumeration<String>)properties.propertyNames();
231
232 while (enu.hasMoreElements()) {
233 String key = enu.nextElement();
234 String value = properties.getProperty(key);
235
236
237
238 if (StringUtil.equalsIgnoreCase(key, "driverClassName")) {
239 key = "driverClass";
240 }
241 else if (StringUtil.equalsIgnoreCase(key, "url")) {
242 key = "jdbcUrl";
243 }
244 else if (StringUtil.equalsIgnoreCase(key, "username")) {
245 key = "user";
246 }
247
248
249
250 if (isPropertyLiferay(key)) {
251 continue;
252 }
253
254
255
256 if (isPropertyDBCP(key)) {
257 continue;
258 }
259
260
261
262 if (isPropertyHikariCP(key)) {
263 continue;
264 }
265
266
267
268 if (isPropertyTomcat(key)) {
269 continue;
270 }
271
272
273
274 try {
275 BeanUtil.setProperty(comboPooledDataSource, key, value);
276 }
277 catch (Exception e) {
278 if (_log.isWarnEnabled()) {
279 _log.warn(
280 "Property " + key + " is an invalid C3PO property");
281 }
282 }
283 }
284
285 registerConnectionPoolMetrics(
286 new C3P0ConnectionPoolMetrics(comboPooledDataSource));
287
288 return comboPooledDataSource;
289 }
290
291 protected DataSource initDataSourceDBCP(Properties properties)
292 throws Exception {
293
294 DataSource dataSource = BasicDataSourceFactory.createDataSource(
295 properties);
296
297 registerConnectionPoolMetrics(
298 new DBCPConnectionPoolMetrics((BasicDataSource)dataSource));
299
300 return dataSource;
301 }
302
303 protected DataSource initDataSourceHikariCP(Properties properties)
304 throws Exception {
305
306 testLiferayPoolProviderClass(_HIKARICP_DATASOURCE_CLASS_NAME);
307
308 Thread currentThread = Thread.currentThread();
309
310 ClassLoader contextClassLoader = currentThread.getContextClassLoader();
311
312 Class<?> hikariDataSourceClazz = contextClassLoader.loadClass(
313 _HIKARICP_DATASOURCE_CLASS_NAME);
314
315 Object hikariDataSource = hikariDataSourceClazz.newInstance();
316
317 for (Map.Entry<Object, Object> entry : properties.entrySet()) {
318 String key = (String)entry.getKey();
319 String value = (String)entry.getValue();
320
321
322
323 if (StringUtil.equalsIgnoreCase(key, "url")) {
324 key = "jdbcUrl";
325 }
326
327
328
329 if (isPropertyLiferay(key)) {
330 continue;
331 }
332
333
334
335 if (isPropertyC3PO(key)) {
336 continue;
337 }
338
339
340
341 if (isPropertyDBCP(key)) {
342 continue;
343 }
344
345
346
347 if (isPropertyTomcat(key)) {
348 continue;
349 }
350
351
352
353 try {
354 BeanUtil.setProperty(hikariDataSource, key, value);
355 }
356 catch (Exception e) {
357 if (_log.isWarnEnabled()) {
358 _log.warn(
359 "Property " + key + " is an invalid HikariCP property");
360 }
361 }
362 }
363
364 registerConnectionPoolMetrics(
365 new HikariConnectionPoolMetrics(hikariDataSource));
366
367 return (DataSource)hikariDataSource;
368 }
369
370 protected DataSource initDataSourceTomcat(Properties properties)
371 throws Exception {
372
373 PoolProperties poolProperties = new PoolProperties();
374
375 for (Map.Entry<Object, Object> entry : properties.entrySet()) {
376 String key = (String)entry.getKey();
377 String value = (String)entry.getValue();
378
379
380
381 if (isPropertyLiferay(key)) {
382 continue;
383 }
384
385
386
387 if (isPropertyC3PO(key)) {
388 continue;
389 }
390
391
392
393 if (isPropertyHikariCP(key)) {
394 continue;
395 }
396
397
398
399 try {
400 BeanUtil.setProperty(poolProperties, key, value);
401 }
402 catch (Exception e) {
403 if (_log.isWarnEnabled()) {
404 _log.warn(
405 "Property " + key + " is an invalid Tomcat JDBC " +
406 "property");
407 }
408 }
409 }
410
411 String poolName = StringUtil.randomString();
412
413 poolProperties.setName(poolName);
414
415 org.apache.tomcat.jdbc.pool.DataSource dataSource =
416 new org.apache.tomcat.jdbc.pool.DataSource(poolProperties);
417
418 if (poolProperties.isJmxEnabled()) {
419 Registry registry = RegistryUtil.getRegistry();
420
421 _serviceTracker = registry.trackServices(
422 MBeanServer.class,
423 new MBeanServerServiceTrackerCustomizer(dataSource, poolName));
424
425 _serviceTracker.open();
426 }
427
428 registerConnectionPoolMetrics(
429 new TomcatConnectionPoolMetrics(dataSource));
430
431 return dataSource;
432 }
433
434 protected boolean isPropertyC3PO(String key) {
435 if (StringUtil.equalsIgnoreCase(key, "acquireIncrement") ||
436 StringUtil.equalsIgnoreCase(key, "acquireRetryAttempts") ||
437 StringUtil.equalsIgnoreCase(key, "acquireRetryDelay") ||
438 StringUtil.equalsIgnoreCase(key, "connectionCustomizerClassName") ||
439 StringUtil.equalsIgnoreCase(key, "idleConnectionTestPeriod") ||
440 StringUtil.equalsIgnoreCase(key, "initialPoolSize") ||
441 StringUtil.equalsIgnoreCase(key, "maxIdleTime") ||
442 StringUtil.equalsIgnoreCase(key, "maxPoolSize") ||
443 StringUtil.equalsIgnoreCase(key, "minPoolSize") ||
444 StringUtil.equalsIgnoreCase(key, "numHelperThreads") ||
445 StringUtil.equalsIgnoreCase(key, "preferredTestQuery")) {
446
447 return true;
448 }
449
450 return false;
451 }
452
453 protected boolean isPropertyDBCP(String key) {
454 if (StringUtil.equalsIgnoreCase(key, "defaultTransactionIsolation") ||
455 StringUtil.equalsIgnoreCase(key, "maxActive") ||
456 StringUtil.equalsIgnoreCase(key, "minIdle") ||
457 StringUtil.equalsIgnoreCase(key, "removeAbandonedTimeout")) {
458
459 return true;
460 }
461
462 return false;
463 }
464
465 protected boolean isPropertyHikariCP(String key) {
466 if (StringUtil.equalsIgnoreCase(key, "autoCommit") ||
467 StringUtil.equalsIgnoreCase(key, "connectionTestQuery") ||
468 StringUtil.equalsIgnoreCase(key, "connectionTimeout") ||
469 StringUtil.equalsIgnoreCase(key, "idleTimeout") ||
470 StringUtil.equalsIgnoreCase(key, "initializationFailFast") ||
471 StringUtil.equalsIgnoreCase(key, "maximumPoolSize") ||
472 StringUtil.equalsIgnoreCase(key, "maxLifetime") ||
473 StringUtil.equalsIgnoreCase(key, "minimumIdle") ||
474 StringUtil.equalsIgnoreCase(key, "registerMbeans")) {
475
476 return true;
477 }
478
479 return false;
480 }
481
482 protected boolean isPropertyLiferay(String key) {
483 if (StringUtil.equalsIgnoreCase(key, "jndi.name") ||
484 StringUtil.equalsIgnoreCase(key, "liferay.pool.provider")) {
485
486 return true;
487 }
488
489 return false;
490 }
491
492 protected boolean isPropertyTomcat(String key) {
493 if (StringUtil.equalsIgnoreCase(key, "fairQueue") ||
494 StringUtil.equalsIgnoreCase(key, "jdbcInterceptors") ||
495 StringUtil.equalsIgnoreCase(key, "jmxEnabled") ||
496 StringUtil.equalsIgnoreCase(key, "timeBetweenEvictionRunsMillis") ||
497 StringUtil.equalsIgnoreCase(key, "useEquals")) {
498
499 return true;
500 }
501
502 return false;
503 }
504
505 protected void registerConnectionPoolMetrics(
506 ConnectionPoolMetrics connectionPoolMetrics) {
507
508 Registry registry = RegistryUtil.getRegistry();
509
510 registry.registerService(
511 ConnectionPoolMetrics.class, connectionPoolMetrics);
512 }
513
514 protected void testDatabaseClass(Properties properties) throws Exception {
515 String driverClassName = properties.getProperty("driverClassName");
516
517 try {
518 Class.forName(driverClassName);
519 }
520 catch (ClassNotFoundException cnfe) {
521 if (!ServerDetector.isJetty() && !ServerDetector.isTomcat()) {
522 throw cnfe;
523 }
524
525 String url = PropsUtil.get(
526 PropsKeys.SETUP_DATABASE_JAR_URL, new Filter(driverClassName));
527 String name = PropsUtil.get(
528 PropsKeys.SETUP_DATABASE_JAR_NAME, new Filter(driverClassName));
529
530 if (Validator.isNull(url) || Validator.isNull(name)) {
531 throw cnfe;
532 }
533
534 ClassLoader classLoader = SystemException.class.getClassLoader();
535
536 if (!(classLoader instanceof URLClassLoader)) {
537 _log.error(
538 "Unable to install JAR because the system class loader " +
539 "is not an instance of URLClassLoader");
540
541 return;
542 }
543
544 JarUtil.downloadAndInstallJar(
545 new URL(url), PropsValues.LIFERAY_LIB_GLOBAL_DIR, name,
546 (URLClassLoader)classLoader);
547 }
548 }
549
550 protected void testLiferayPoolProviderClass(String className)
551 throws Exception {
552
553 try {
554 Class.forName(className);
555 }
556 catch (ClassNotFoundException cnfe) {
557 if (!ServerDetector.isJetty() && !ServerDetector.isTomcat()) {
558 throw cnfe;
559 }
560
561 String url = PropsUtil.get(
562 PropsKeys.SETUP_LIFERAY_POOL_PROVIDER_JAR_URL,
563 new Filter(PropsValues.JDBC_DEFAULT_LIFERAY_POOL_PROVIDER));
564 String name = PropsUtil.get(
565 PropsKeys.SETUP_LIFERAY_POOL_PROVIDER_JAR_NAME,
566 new Filter(PropsValues.JDBC_DEFAULT_LIFERAY_POOL_PROVIDER));
567
568 if (Validator.isNull(url) || Validator.isNull(name)) {
569 throw cnfe;
570 }
571
572 ClassLoader classLoader = ClassLoaderUtil.getPortalClassLoader();
573
574 if (!(classLoader instanceof URLClassLoader)) {
575 _log.error(
576 "Unable to install JAR because the portal class loader " +
577 "is not an instance of URLClassLoader");
578
579 return;
580 }
581
582 JarUtil.downloadAndInstallJar(
583 new URL(url), PropsValues.LIFERAY_LIB_PORTAL_DIR, name,
584 (URLClassLoader)classLoader);
585 }
586 }
587
588 private static final String _HIKARICP_DATASOURCE_CLASS_NAME =
589 "com.zaxxer.hikari.HikariDataSource";
590
591 private static final String _TOMCAT_JDBC_POOL_OBJECT_NAME_PREFIX =
592 "TomcatJDBCPool:type=ConnectionPool,name=";
593
594 private static final Log _log = LogFactoryUtil.getLog(
595 DataSourceFactoryImpl.class);
596
597 private static final PACL _pacl = new NoPACL();
598
599 private ServiceTracker <MBeanServer, MBeanServer> _serviceTracker;
600
601 private static class MBeanServerServiceTrackerCustomizer
602 implements ServiceTrackerCustomizer<MBeanServer, MBeanServer> {
603
604 public MBeanServerServiceTrackerCustomizer(
605 org.apache.tomcat.jdbc.pool.DataSource dataSource,
606 String poolName)
607 throws MalformedObjectNameException {
608
609 _dataSource = dataSource;
610 _objectName = new ObjectName(
611 _TOMCAT_JDBC_POOL_OBJECT_NAME_PREFIX + poolName);
612 }
613
614 @Override
615 public MBeanServer addingService(
616 ServiceReference<MBeanServer> serviceReference) {
617
618 Registry registry = RegistryUtil.getRegistry();
619
620 MBeanServer mBeanServer = registry.getService(serviceReference);
621
622 try {
623 org.apache.tomcat.jdbc.pool.ConnectionPool jdbcConnectionPool =
624 _dataSource.createPool();
625
626 ConnectionPool jmxConnectionPool =
627 jdbcConnectionPool.getJmxPool();
628
629 mBeanServer.registerMBean(jmxConnectionPool, _objectName);
630 }
631 catch (Exception e) {
632 _log.error(e, e);
633 }
634
635 return mBeanServer;
636 }
637
638 @Override
639 public void modifiedService(
640 ServiceReference<MBeanServer> serviceReference,
641 MBeanServer mBeanServer) {
642 }
643
644 @Override
645 public void removedService(
646 ServiceReference<MBeanServer> serviceReference,
647 MBeanServer mBeanServer) {
648
649 Registry registry = RegistryUtil.getRegistry();
650
651 registry.ungetService(serviceReference);
652
653 try {
654 mBeanServer.unregisterMBean(_objectName);
655 }
656 catch (Exception e) {
657 _log.error(e, e);
658 }
659 }
660
661 private final org.apache.tomcat.jdbc.pool.DataSource _dataSource;
662 private final ObjectName _objectName;
663
664 }
665
666 private static class NoPACL implements PACL {
667
668 @Override
669 public DataSource getDataSource(DataSource dataSource) {
670 return dataSource;
671 }
672
673 }
674
675 }