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