001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.dao.jdbc;
016    
017    import com.liferay.portal.kernel.dao.jdbc.DataSourceFactory;
018    import com.liferay.portal.kernel.jndi.JNDIUtil;
019    import com.liferay.portal.kernel.log.Log;
020    import com.liferay.portal.kernel.log.LogFactoryUtil;
021    import com.liferay.portal.kernel.util.PropertiesUtil;
022    import com.liferay.portal.kernel.util.PropsKeys;
023    import com.liferay.portal.kernel.util.SortedProperties;
024    import com.liferay.portal.kernel.util.Validator;
025    import com.liferay.portal.util.PropsUtil;
026    import com.liferay.portal.util.PropsValues;
027    import com.liferay.util.PwdGenerator;
028    
029    import com.mchange.v2.c3p0.ComboPooledDataSource;
030    
031    import java.lang.management.ManagementFactory;
032    
033    import java.util.Enumeration;
034    import java.util.Map;
035    import java.util.Properties;
036    
037    import javax.management.MBeanServer;
038    import javax.management.ObjectName;
039    
040    import javax.naming.Context;
041    import javax.naming.InitialContext;
042    
043    import javax.sql.DataSource;
044    
045    import jodd.bean.BeanUtil;
046    
047    import org.apache.commons.dbcp.BasicDataSourceFactory;
048    import org.apache.tomcat.jdbc.pool.PoolProperties;
049    import org.apache.tomcat.jdbc.pool.jmx.ConnectionPool;
050    
051    import uk.org.primrose.pool.datasource.GenericDataSourceFactory;
052    
053    /**
054     * @author Brian Wing Shun Chan
055     * @author Shuyang Zhou
056     */
057    public class DataSourceFactoryImpl implements DataSourceFactory {
058    
059            public void destroyDataSource(DataSource dataSource) throws Exception {
060                    if (dataSource instanceof ComboPooledDataSource) {
061                            ComboPooledDataSource comboPooledDataSource =
062                                    (ComboPooledDataSource)dataSource;
063    
064                            comboPooledDataSource.close();
065                    }
066                    else if (dataSource instanceof org.apache.tomcat.jdbc.pool.DataSource) {
067                            org.apache.tomcat.jdbc.pool.DataSource tomcatDataSource =
068                                    (org.apache.tomcat.jdbc.pool.DataSource)dataSource;
069    
070                            tomcatDataSource.close();
071                    }
072            }
073    
074            public DataSource initDataSource(Properties properties) throws Exception {
075                    Properties defaultProperties = PropsUtil.getProperties(
076                            "jdbc.default.", true);
077    
078                    PropertiesUtil.merge(defaultProperties, properties);
079    
080                    properties = defaultProperties;
081    
082                    String jndiName = properties.getProperty("jndi.name");
083    
084                    if (Validator.isNotNull(jndiName)) {
085                            try {
086                                    Properties jndiEnvironmentProperties = PropsUtil.getProperties(
087                                            PropsKeys.JNDI_ENVIRONMENT, true);
088    
089                                    Context context = new InitialContext(jndiEnvironmentProperties);
090    
091                                    return (DataSource)JNDIUtil.lookup(context, jndiName);
092                            }
093                            catch (Exception e) {
094                                    _log.error("Unable to lookup " + jndiName, e);
095                            }
096                    }
097    
098                    DataSource dataSource = null;
099    
100                    String liferayPoolProvider =
101                            PropsValues.JDBC_DEFAULT_LIFERAY_POOL_PROVIDER;
102    
103                    if (liferayPoolProvider.equalsIgnoreCase("c3p0") ||
104                            liferayPoolProvider.equalsIgnoreCase("c3po")) {
105    
106                            if (_log.isDebugEnabled()) {
107                                    _log.debug("Initializing C3P0 data source");
108                            }
109    
110                            dataSource = initDataSourceC3PO(properties);
111                    }
112                    else if (liferayPoolProvider.equalsIgnoreCase("dbcp")) {
113                            if (_log.isDebugEnabled()) {
114                                    _log.debug("Initializing DBCP data source");
115                            }
116    
117                            dataSource = initDataSourceDBCP(properties);
118                    }
119                    else if (liferayPoolProvider.equalsIgnoreCase("primrose")) {
120                            if (_log.isDebugEnabled()) {
121                                    _log.debug("Initializing Primrose data source");
122                            }
123    
124                            dataSource = initDataSourcePrimrose(properties);
125                    }
126                    else {
127                            if (_log.isDebugEnabled()) {
128                                    _log.debug("Initializing Tomcat data source");
129                            }
130    
131                            dataSource = initDataSourceTomcat(properties);
132                    }
133    
134                    if (_log.isDebugEnabled()) {
135                            _log.debug(
136                                    "Created data source " + dataSource.getClass().getName());
137    
138                            SortedProperties sortedProperties = new SortedProperties(
139                                    properties);
140    
141                            sortedProperties.list(System.out);
142                    }
143    
144                    return dataSource;
145            }
146    
147            public DataSource initDataSource(
148                            String driverClassName, String url, String userName,
149                            String password)
150                    throws Exception {
151    
152                    Properties properties = new Properties();
153    
154                    properties.setProperty("driverClassName", driverClassName);
155                    properties.setProperty("url", url);
156                    properties.setProperty("username", userName);
157                    properties.setProperty("password", password);
158    
159                    return initDataSource(properties);
160            }
161    
162            protected DataSource initDataSourceC3PO(Properties properties)
163                    throws Exception {
164    
165                    ComboPooledDataSource comboPooledDataSource =
166                            new ComboPooledDataSource();
167    
168                    String identityToken = PwdGenerator.getPassword(PwdGenerator.KEY2, 8);
169    
170                    comboPooledDataSource.setIdentityToken(identityToken);
171    
172                    Enumeration<String> enu =
173                            (Enumeration<String>)properties.propertyNames();
174    
175                    while (enu.hasMoreElements()) {
176                            String key = enu.nextElement();
177                            String value = properties.getProperty(key);
178    
179                            // Map org.apache.commons.dbcp.BasicDataSource to C3PO
180    
181                            if (key.equalsIgnoreCase("driverClassName")) {
182                                    key = "driverClass";
183                            }
184                            else if (key.equalsIgnoreCase("url")) {
185                                    key = "jdbcUrl";
186                            }
187                            else if (key.equalsIgnoreCase("username")) {
188                                    key = "user";
189                            }
190    
191                            // Ignore Liferay properties
192    
193                            if (isPropertyLiferay(key)) {
194                                    continue;
195                            }
196    
197                            // Ignore DBCP properties
198    
199                            if (isPropertyDBCP(key)) {
200                                    continue;
201                            }
202    
203                            // Ignore Primrose properties
204    
205                            if (isPropertyPrimrose(key)) {
206                                    continue;
207                            }
208    
209                            // Ignore Tomcat
210    
211                            if (isPropertyTomcat(key)) {
212                                    continue;
213                            }
214    
215                            try {
216                                    BeanUtil.setProperty(comboPooledDataSource, key, value);
217                            }
218                            catch (Exception e) {
219                                    if (_log.isWarnEnabled()) {
220                                            _log.warn(
221                                                    "Property " + key + " is not a valid C3PO property");
222                                    }
223                            }
224                    }
225    
226                    return comboPooledDataSource;
227            }
228    
229            protected DataSource initDataSourceDBCP(Properties properties)
230                    throws Exception {
231    
232                    return BasicDataSourceFactory.createDataSource(properties);
233            }
234    
235            protected DataSource initDataSourcePrimrose(Properties properties)
236                    throws Exception {
237    
238                    String poolName = PwdGenerator.getPassword(PwdGenerator.KEY2, 8);
239    
240                    properties.setProperty("poolName", poolName);
241    
242                    Enumeration<String> enu =
243                            (Enumeration<String>)properties.propertyNames();
244    
245                    while (enu.hasMoreElements()) {
246                            String key = enu.nextElement();
247    
248                            String value = properties.getProperty(key);
249    
250                            // Map org.apache.commons.dbcp.BasicDataSource to Primrose
251    
252                            if (key.equalsIgnoreCase("driverClassName")) {
253                                    key = "driverClass";
254                            }
255                            else if (key.equalsIgnoreCase("url")) {
256                                    key = "driverURL";
257                            }
258                            else if (key.equalsIgnoreCase("username")) {
259                                    key = "user";
260                            }
261    
262                            properties.setProperty(key, value);
263                    }
264    
265                    GenericDataSourceFactory genericDataSourceFactory =
266                            new GenericDataSourceFactory();
267    
268                    return genericDataSourceFactory.loadPool(poolName, properties);
269            }
270    
271            protected DataSource initDataSourceTomcat(Properties properties)
272                    throws Exception {
273    
274                    PoolProperties poolProperties = new PoolProperties();
275    
276                    for (Map.Entry<Object, Object> entry : properties.entrySet()) {
277                            String key = (String)entry.getKey();
278                            String value = (String)entry.getValue();
279    
280                            // Ignore Liferay properties
281    
282                            if (isPropertyLiferay(key)) {
283                                    continue;
284                            }
285    
286                            // Ignore C3P0 properties
287    
288                            if (isPropertyC3PO(key)) {
289                                    continue;
290                            }
291    
292                            // Ignore Primrose properties
293    
294                            if (isPropertyPrimrose(key)) {
295                                    continue;
296                            }
297    
298                            try {
299                                    BeanUtil.setProperty(poolProperties, key, value);
300                            }
301                            catch (Exception e) {
302                                    if (_log.isWarnEnabled()) {
303                                            _log.warn(
304                                                    "Property " + key + " is not a valid Tomcat JDBC " +
305                                                            "Connection Pool property");
306                                    }
307                            }
308                    }
309    
310                    String poolName = PwdGenerator.getPassword(PwdGenerator.KEY2, 8);
311    
312                    poolProperties.setName(poolName);
313    
314                    org.apache.tomcat.jdbc.pool.DataSource dataSource =
315                            new org.apache.tomcat.jdbc.pool.DataSource(poolProperties);
316    
317                    if (poolProperties.isJmxEnabled()) {
318                            org.apache.tomcat.jdbc.pool.ConnectionPool jdbcConnectionPool =
319                                    dataSource.createPool();
320    
321                            ConnectionPool jmxConnectionPool = jdbcConnectionPool.getJmxPool();
322    
323                            MBeanServer mBeanServer =
324                                    ManagementFactory.getPlatformMBeanServer();
325    
326                            ObjectName objectName = new ObjectName(
327                                    _TOMCAT_JDBC_POOL_OBJECT_NAME_PREFIX + poolName);
328    
329                            mBeanServer.registerMBean(jmxConnectionPool, objectName);
330                    }
331    
332                    return dataSource;
333            }
334    
335            protected boolean isPropertyC3PO(String key) {
336                    if (key.equalsIgnoreCase("acquireIncrement") ||
337                            key.equalsIgnoreCase("acquireRetryAttempts") ||
338                            key.equalsIgnoreCase("acquireRetryDelay") ||
339                            key.equalsIgnoreCase("connectionCustomizerClassName") ||
340                            key.equalsIgnoreCase("idleConnectionTestPeriod") ||
341                            key.equalsIgnoreCase("maxIdleTime") ||
342                            key.equalsIgnoreCase("maxPoolSize") ||
343                            key.equalsIgnoreCase("minPoolSize") ||
344                            key.equalsIgnoreCase("numHelperThreads") ||
345                            key.equalsIgnoreCase("preferredTestQuery")) {
346    
347                            return true;
348                    }
349                    else {
350                            return false;
351                    }
352            }
353    
354            protected boolean isPropertyDBCP(String key) {
355                    if (key.equalsIgnoreCase("defaultTransactionIsolation") ||
356                            key.equalsIgnoreCase("maxActive") ||
357                            key.equalsIgnoreCase("minIdle") ||
358                            key.equalsIgnoreCase("removeAbandonedTimeout")) {
359    
360                            return true;
361                    }
362                    else {
363                            return false;
364                    }
365            }
366    
367            protected boolean isPropertyLiferay(String key) {
368                    if (key.equalsIgnoreCase("jndi.name") ||
369                            key.equalsIgnoreCase("liferay.pool.provider")) {
370    
371                            return true;
372                    }
373                    else {
374                            return false;
375                    }
376            }
377    
378            protected boolean isPropertyPrimrose(String key) {
379                    if (key.equalsIgnoreCase("base") ||
380                            key.equalsIgnoreCase("connectionTransactionIsolation") ||
381                            key.equalsIgnoreCase("idleTime") ||
382                            key.equalsIgnoreCase("numberOfConnectionsToInitializeWith")) {
383    
384                            return true;
385                    }
386                    else {
387                            return false;
388                    }
389            }
390    
391            protected boolean isPropertyTomcat(String key) {
392                    if (key.equalsIgnoreCase("fairQueue") ||
393                            key.equalsIgnoreCase("jdbcInterceptors") ||
394                            key.equalsIgnoreCase("jmxEnabled") ||
395                            key.equalsIgnoreCase("timeBetweenEvictionRunsMillis") ||
396                            key.equalsIgnoreCase("useEquals")) {
397    
398                            return true;
399                    }
400                    else {
401                            return false;
402                    }
403            }
404    
405            private static final String _TOMCAT_JDBC_POOL_OBJECT_NAME_PREFIX =
406                    "TomcatJDBCPool:type=ConnectionPool,name=";
407    
408            private static Log _log = LogFactoryUtil.getLog(
409                    DataSourceFactoryImpl.class);
410    
411    }