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 else if (StringUtil.equalsIgnoreCase(
299 key, "hikariConnectionCustomizerClassName")) {
300
301 key = "connectionCustomizerClassName";
302 }
303
304
305
306 if (isPropertyLiferay(key)) {
307 continue;
308 }
309
310
311
312 if (isPropertyC3PO(key)) {
313 continue;
314 }
315
316
317
318 if (isPropertyDBCP(key)) {
319 continue;
320 }
321
322
323
324 if (isPropertyTomcat(key)) {
325 continue;
326 }
327
328
329
330 try {
331 BeanUtil.setProperty(hikariDataSource, key, value);
332 }
333 catch (Exception e) {
334 if (_log.isWarnEnabled()) {
335 _log.warn(
336 "Property " + key + " is an invalid HikariCP property");
337 }
338 }
339 }
340
341 return (DataSource)hikariDataSource;
342 }
343
344 protected DataSource initDataSourceTomcat(Properties properties)
345 throws Exception {
346
347 PoolProperties poolProperties = new PoolProperties();
348
349 for (Map.Entry<Object, Object> entry : properties.entrySet()) {
350 String key = (String)entry.getKey();
351 String value = (String)entry.getValue();
352
353
354
355 if (isPropertyLiferay(key)) {
356 continue;
357 }
358
359
360
361 if (isPropertyC3PO(key)) {
362 continue;
363 }
364
365
366
367 if (isPropertyHikariCP(key)) {
368 continue;
369 }
370
371
372
373 try {
374 BeanUtil.setProperty(poolProperties, key, value);
375 }
376 catch (Exception e) {
377 if (_log.isWarnEnabled()) {
378 _log.warn(
379 "Property " + key + " is an invalid Tomcat JDBC " +
380 "property");
381 }
382 }
383 }
384
385 String poolName = StringUtil.randomString();
386
387 poolProperties.setName(poolName);
388
389 org.apache.tomcat.jdbc.pool.DataSource dataSource =
390 new org.apache.tomcat.jdbc.pool.DataSource(poolProperties);
391
392 if (poolProperties.isJmxEnabled()) {
393 Registry registry = RegistryUtil.getRegistry();
394
395 _serviceTracker = registry.trackServices(
396 MBeanServer.class,
397 new MBeanServerServiceTrackerCustomizer(dataSource, poolName));
398
399 _serviceTracker.open();
400 }
401
402 return dataSource;
403 }
404
405 protected boolean isPropertyC3PO(String key) {
406 if (StringUtil.equalsIgnoreCase(key, "acquireIncrement") ||
407 StringUtil.equalsIgnoreCase(key, "acquireRetryAttempts") ||
408 StringUtil.equalsIgnoreCase(key, "acquireRetryDelay") ||
409 StringUtil.equalsIgnoreCase(key, "connectionCustomizerClassName") ||
410 StringUtil.equalsIgnoreCase(key, "idleConnectionTestPeriod") ||
411 StringUtil.equalsIgnoreCase(key, "maxIdleTime") ||
412 StringUtil.equalsIgnoreCase(key, "maxPoolSize") ||
413 StringUtil.equalsIgnoreCase(key, "minPoolSize") ||
414 StringUtil.equalsIgnoreCase(key, "numHelperThreads") ||
415 StringUtil.equalsIgnoreCase(key, "preferredTestQuery")) {
416
417 return true;
418 }
419 else {
420 return false;
421 }
422 }
423
424 protected boolean isPropertyDBCP(String key) {
425 if (StringUtil.equalsIgnoreCase(key, "defaultTransactionIsolation") ||
426 StringUtil.equalsIgnoreCase(key, "maxActive") ||
427 StringUtil.equalsIgnoreCase(key, "minIdle") ||
428 StringUtil.equalsIgnoreCase(key, "removeAbandonedTimeout")) {
429
430 return true;
431 }
432 else {
433 return false;
434 }
435 }
436
437 protected boolean isPropertyHikariCP(String key) {
438 if (StringUtil.equalsIgnoreCase(key, "autoCommit") ||
439 StringUtil.equalsIgnoreCase(key, "connectionTimeout") ||
440 StringUtil.equalsIgnoreCase(
441 key, "hikariConnectionCustomizerClassName") ||
442 StringUtil.equalsIgnoreCase(key, "idleTimeout") ||
443 StringUtil.equalsIgnoreCase(key, "maximumPoolSize") ||
444 StringUtil.equalsIgnoreCase(key, "maxLifetime") ||
445 StringUtil.equalsIgnoreCase(key, "minimumIdle") ||
446 StringUtil.equalsIgnoreCase(key, "registerMbeans")) {
447
448 return true;
449 }
450 else {
451 return false;
452 }
453 }
454
455 protected boolean isPropertyLiferay(String key) {
456 if (StringUtil.equalsIgnoreCase(key, "jndi.name") ||
457 StringUtil.equalsIgnoreCase(key, "liferay.pool.provider")) {
458
459 return true;
460 }
461 else {
462 return false;
463 }
464 }
465
466 protected boolean isPropertyTomcat(String key) {
467 if (StringUtil.equalsIgnoreCase(key, "fairQueue") ||
468 StringUtil.equalsIgnoreCase(key, "jdbcInterceptors") ||
469 StringUtil.equalsIgnoreCase(key, "jmxEnabled") ||
470 StringUtil.equalsIgnoreCase(key, "timeBetweenEvictionRunsMillis") ||
471 StringUtil.equalsIgnoreCase(key, "useEquals")) {
472
473 return true;
474 }
475 else {
476 return false;
477 }
478 }
479
480 protected void testDatabaseClass(Properties properties) throws Exception {
481 String driverClassName = properties.getProperty("driverClassName");
482
483 try {
484 Class.forName(driverClassName);
485 }
486 catch (ClassNotFoundException cnfe) {
487 if (!ServerDetector.isJetty() && !ServerDetector.isTomcat()) {
488 throw cnfe;
489 }
490
491 String url = PropsUtil.get(
492 PropsKeys.SETUP_DATABASE_JAR_URL, new Filter(driverClassName));
493 String name = PropsUtil.get(
494 PropsKeys.SETUP_DATABASE_JAR_NAME, new Filter(driverClassName));
495
496 if (Validator.isNull(url) || Validator.isNull(name)) {
497 throw cnfe;
498 }
499
500 ClassLoader classLoader = SystemException.class.getClassLoader();
501
502 if (!(classLoader instanceof URLClassLoader)) {
503 _log.error(
504 "Unable to install JAR because the system class loader " +
505 "is not an instance of URLClassLoader");
506
507 return;
508 }
509
510 JarUtil.downloadAndInstallJar(
511 new URL(url), PropsValues.LIFERAY_LIB_GLOBAL_DIR, name,
512 (URLClassLoader)classLoader);
513 }
514 }
515
516 protected void testLiferayPoolProviderClass(String className)
517 throws Exception {
518
519 try {
520 Class.forName(className);
521 }
522 catch (ClassNotFoundException cnfe) {
523 if (!ServerDetector.isJetty() && !ServerDetector.isTomcat()) {
524 throw cnfe;
525 }
526
527 String url = PropsUtil.get(
528 PropsKeys.SETUP_LIFERAY_POOL_PROVIDER_JAR_URL,
529 new Filter(PropsValues.JDBC_DEFAULT_LIFERAY_POOL_PROVIDER));
530 String name = PropsUtil.get(
531 PropsKeys.SETUP_LIFERAY_POOL_PROVIDER_JAR_NAME,
532 new Filter(PropsValues.JDBC_DEFAULT_LIFERAY_POOL_PROVIDER));
533
534 if (Validator.isNull(url) || Validator.isNull(name)) {
535 throw cnfe;
536 }
537
538 ClassLoader classLoader = ClassLoaderUtil.getPortalClassLoader();
539
540 if (!(classLoader instanceof URLClassLoader)) {
541 _log.error(
542 "Unable to install JAR because the portal class loader " +
543 "is not an instance of URLClassLoader");
544
545 return;
546 }
547
548 JarUtil.downloadAndInstallJar(
549 new URL(url), PropsValues.LIFERAY_LIB_PORTAL_DIR, name,
550 (URLClassLoader)classLoader);
551 }
552 }
553
554 private static final String _HIKARICP_DATASOURCE_CLASS_NAME =
555 "com.zaxxer.hikari.HikariDataSource";
556
557 private static final String _TOMCAT_JDBC_POOL_OBJECT_NAME_PREFIX =
558 "TomcatJDBCPool:type=ConnectionPool,name=";
559
560 private static final Log _log = LogFactoryUtil.getLog(
561 DataSourceFactoryImpl.class);
562
563 private static final PACL _pacl = new NoPACL();
564
565 private ServiceTracker <MBeanServer, MBeanServer> _serviceTracker;
566
567 private static class NoPACL implements PACL {
568
569 @Override
570 public DataSource getDataSource(DataSource dataSource) {
571 return dataSource;
572 }
573
574 }
575
576 private class MBeanServerServiceTrackerCustomizer
577 implements ServiceTrackerCustomizer<MBeanServer, MBeanServer> {
578
579 public MBeanServerServiceTrackerCustomizer(
580 org.apache.tomcat.jdbc.pool.DataSource dataSource,
581 String poolName)
582 throws MalformedObjectNameException {
583
584 _dataSource = dataSource;
585 _objectName = new ObjectName(
586 _TOMCAT_JDBC_POOL_OBJECT_NAME_PREFIX + poolName);
587 }
588
589 @Override
590 public MBeanServer addingService(
591 ServiceReference<MBeanServer> serviceReference) {
592
593 Registry registry = RegistryUtil.getRegistry();
594
595 MBeanServer mBeanServer = registry.getService(serviceReference);
596
597 try {
598 org.apache.tomcat.jdbc.pool.ConnectionPool jdbcConnectionPool =
599 _dataSource.createPool();
600
601 ConnectionPool jmxConnectionPool =
602 jdbcConnectionPool.getJmxPool();
603
604 mBeanServer.registerMBean(jmxConnectionPool, _objectName);
605 }
606 catch (Exception e) {
607 _log.error(e, e);
608 }
609
610 return mBeanServer;
611 }
612
613 @Override
614 public void modifiedService(
615 ServiceReference<MBeanServer> serviceReference,
616 MBeanServer mBeanServer) {
617 }
618
619 @Override
620 public void removedService(
621 ServiceReference<MBeanServer> serviceReference,
622 MBeanServer mBeanServer) {
623
624 Registry registry = RegistryUtil.getRegistry();
625
626 registry.ungetService(serviceReference);
627
628 try {
629 mBeanServer.unregisterMBean(_objectName);
630 }
631 catch (Exception e) {
632 _log.error(e, e);
633 }
634 }
635
636 private final org.apache.tomcat.jdbc.pool.DataSource _dataSource;
637 private final ObjectName _objectName;
638
639 }
640
641 }