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