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