001    /**
002     * Copyright (c) 2000-2013 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.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.jndi.JNDIUtil;
021    import com.liferay.portal.kernel.log.Log;
022    import com.liferay.portal.kernel.log.LogFactoryUtil;
023    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
024    import com.liferay.portal.kernel.util.FileUtil;
025    import com.liferay.portal.kernel.util.HttpUtil;
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.Validator;
031    import com.liferay.portal.util.FileImpl;
032    import com.liferay.portal.util.HttpImpl;
033    import com.liferay.portal.util.JarUtil;
034    import com.liferay.portal.util.PropsUtil;
035    import com.liferay.portal.util.PropsValues;
036    import com.liferay.util.PwdGenerator;
037    
038    import com.mchange.v2.c3p0.ComboPooledDataSource;
039    
040    import java.lang.management.ManagementFactory;
041    
042    import java.util.Enumeration;
043    import java.util.Map;
044    import java.util.Properties;
045    
046    import javax.management.MBeanServer;
047    import javax.management.ObjectName;
048    
049    import javax.naming.Context;
050    import javax.naming.InitialContext;
051    
052    import javax.sql.DataSource;
053    
054    import jodd.bean.BeanUtil;
055    
056    import org.apache.commons.dbcp.BasicDataSourceFactory;
057    import org.apache.tomcat.jdbc.pool.PoolProperties;
058    import org.apache.tomcat.jdbc.pool.jmx.ConnectionPool;
059    
060    /**
061     * @author Brian Wing Shun Chan
062     * @author Shuyang Zhou
063     */
064    @DoPrivileged
065    public class DataSourceFactoryImpl implements DataSourceFactory {
066    
067            @Override
068            public void destroyDataSource(DataSource dataSource) throws Exception {
069                    while (dataSource instanceof DataSourceWrapper) {
070                            DataSourceWrapper dataSourceWrapper = (DataSourceWrapper)dataSource;
071    
072                            dataSource = dataSourceWrapper.getWrappedDataSource();
073                    }
074    
075                    if (dataSource instanceof ComboPooledDataSource) {
076                            ComboPooledDataSource comboPooledDataSource =
077                                    (ComboPooledDataSource)dataSource;
078    
079                            comboPooledDataSource.close();
080                    }
081                    else if (dataSource instanceof org.apache.tomcat.jdbc.pool.DataSource) {
082                            org.apache.tomcat.jdbc.pool.DataSource tomcatDataSource =
083                                    (org.apache.tomcat.jdbc.pool.DataSource)dataSource;
084    
085                            tomcatDataSource.close();
086                    }
087            }
088    
089            @Override
090            public DataSource initDataSource(Properties properties) throws Exception {
091                    Properties defaultProperties = PropsUtil.getProperties(
092                            "jdbc.default.", true);
093    
094                    PropertiesUtil.merge(defaultProperties, properties);
095    
096                    properties = defaultProperties;
097    
098                    String jndiName = properties.getProperty("jndi.name");
099    
100                    if (Validator.isNotNull(jndiName)) {
101                            try {
102                                    Properties jndiEnvironmentProperties = PropsUtil.getProperties(
103                                            PropsKeys.JNDI_ENVIRONMENT, true);
104    
105                                    Context context = new InitialContext(jndiEnvironmentProperties);
106    
107                                    return (DataSource)JNDIUtil.lookup(context, jndiName);
108                            }
109                            catch (Exception e) {
110                                    _log.error("Unable to lookup " + jndiName, e);
111                            }
112                    }
113    
114                    if (_log.isDebugEnabled()) {
115                            _log.debug("Data source properties:\n");
116    
117                            SortedProperties sortedProperties = new SortedProperties(
118                                    properties);
119    
120                            _log.debug(PropertiesUtil.toString(sortedProperties));
121                    }
122    
123                    testClassForName(properties);
124    
125                    DataSource dataSource = null;
126    
127                    String liferayPoolProvider =
128                            PropsValues.JDBC_DEFAULT_LIFERAY_POOL_PROVIDER;
129    
130                    if (liferayPoolProvider.equalsIgnoreCase("c3p0") ||
131                            liferayPoolProvider.equalsIgnoreCase("c3po")) {
132    
133                            if (_log.isDebugEnabled()) {
134                                    _log.debug("Initializing C3P0 data source");
135                            }
136    
137                            dataSource = initDataSourceC3PO(properties);
138                    }
139                    else if (liferayPoolProvider.equalsIgnoreCase("dbcp")) {
140                            if (_log.isDebugEnabled()) {
141                                    _log.debug("Initializing DBCP data source");
142                            }
143    
144                            dataSource = initDataSourceDBCP(properties);
145                    }
146                    else {
147                            if (_log.isDebugEnabled()) {
148                                    _log.debug("Initializing Tomcat data source");
149                            }
150    
151                            dataSource = initDataSourceTomcat(properties);
152                    }
153    
154                    if (_log.isDebugEnabled()) {
155                            _log.debug("Created data source " + dataSource.getClass());
156                    }
157    
158                    return _pacl.getDataSource(dataSource);
159            }
160    
161            @Override
162            public DataSource initDataSource(
163                            String driverClassName, String url, String userName,
164                            String password, String jndiName)
165                    throws Exception {
166    
167                    Properties properties = new Properties();
168    
169                    properties.setProperty("driverClassName", driverClassName);
170                    properties.setProperty("url", url);
171                    properties.setProperty("username", userName);
172                    properties.setProperty("password", password);
173                    properties.setProperty("jndi.name", jndiName);
174    
175                    return initDataSource(properties);
176            }
177    
178            protected DataSource initDataSourceC3PO(Properties properties)
179                    throws Exception {
180    
181                    ComboPooledDataSource comboPooledDataSource =
182                            new ComboPooledDataSource();
183    
184                    String identityToken = PwdGenerator.getPassword(PwdGenerator.KEY2, 8);
185    
186                    comboPooledDataSource.setIdentityToken(identityToken);
187    
188                    Enumeration<String> enu =
189                            (Enumeration<String>)properties.propertyNames();
190    
191                    while (enu.hasMoreElements()) {
192                            String key = enu.nextElement();
193                            String value = properties.getProperty(key);
194    
195                            // Map org.apache.commons.dbcp.BasicDataSource to C3PO
196    
197                            if (key.equalsIgnoreCase("driverClassName")) {
198                                    key = "driverClass";
199                            }
200                            else if (key.equalsIgnoreCase("url")) {
201                                    key = "jdbcUrl";
202                            }
203                            else if (key.equalsIgnoreCase("username")) {
204                                    key = "user";
205                            }
206    
207                            // Ignore Liferay properties
208    
209                            if (isPropertyLiferay(key)) {
210                                    continue;
211                            }
212    
213                            // Ignore DBCP properties
214    
215                            if (isPropertyDBCP(key)) {
216                                    continue;
217                            }
218    
219                            // Ignore Tomcat
220    
221                            if (isPropertyTomcat(key)) {
222                                    continue;
223                            }
224    
225                            try {
226                                    BeanUtil.setProperty(comboPooledDataSource, key, value);
227                            }
228                            catch (Exception e) {
229                                    if (_log.isWarnEnabled()) {
230                                            _log.warn(
231                                                    "Property " + key + " is not a valid C3PO property");
232                                    }
233                            }
234                    }
235    
236                    return comboPooledDataSource;
237            }
238    
239            protected DataSource initDataSourceDBCP(Properties properties)
240                    throws Exception {
241    
242                    return BasicDataSourceFactory.createDataSource(properties);
243            }
244    
245            protected DataSource initDataSourceTomcat(Properties properties)
246                    throws Exception {
247    
248                    PoolProperties poolProperties = new PoolProperties();
249    
250                    for (Map.Entry<Object, Object> entry : properties.entrySet()) {
251                            String key = (String)entry.getKey();
252                            String value = (String)entry.getValue();
253    
254                            // Ignore Liferay properties
255    
256                            if (isPropertyLiferay(key)) {
257                                    continue;
258                            }
259    
260                            // Ignore C3P0 properties
261    
262                            if (isPropertyC3PO(key)) {
263                                    continue;
264                            }
265    
266                            try {
267                                    BeanUtil.setProperty(poolProperties, key, value);
268                            }
269                            catch (Exception e) {
270                                    if (_log.isWarnEnabled()) {
271                                            _log.warn(
272                                                    "Property " + key + " is not a valid Tomcat JDBC " +
273                                                            "Connection Pool property");
274                                    }
275                            }
276                    }
277    
278                    String poolName = PwdGenerator.getPassword(PwdGenerator.KEY2, 8);
279    
280                    poolProperties.setName(poolName);
281    
282                    org.apache.tomcat.jdbc.pool.DataSource dataSource =
283                            new org.apache.tomcat.jdbc.pool.DataSource(poolProperties);
284    
285                    if (poolProperties.isJmxEnabled()) {
286                            org.apache.tomcat.jdbc.pool.ConnectionPool jdbcConnectionPool =
287                                    dataSource.createPool();
288    
289                            ConnectionPool jmxConnectionPool = jdbcConnectionPool.getJmxPool();
290    
291                            MBeanServer mBeanServer =
292                                    ManagementFactory.getPlatformMBeanServer();
293    
294                            ObjectName objectName = new ObjectName(
295                                    _TOMCAT_JDBC_POOL_OBJECT_NAME_PREFIX + poolName);
296    
297                            mBeanServer.registerMBean(jmxConnectionPool, objectName);
298                    }
299    
300                    return dataSource;
301            }
302    
303            protected boolean isPropertyC3PO(String key) {
304                    if (key.equalsIgnoreCase("acquireIncrement") ||
305                            key.equalsIgnoreCase("acquireRetryAttempts") ||
306                            key.equalsIgnoreCase("acquireRetryDelay") ||
307                            key.equalsIgnoreCase("connectionCustomizerClassName") ||
308                            key.equalsIgnoreCase("idleConnectionTestPeriod") ||
309                            key.equalsIgnoreCase("maxIdleTime") ||
310                            key.equalsIgnoreCase("maxPoolSize") ||
311                            key.equalsIgnoreCase("minPoolSize") ||
312                            key.equalsIgnoreCase("numHelperThreads") ||
313                            key.equalsIgnoreCase("preferredTestQuery")) {
314    
315                            return true;
316                    }
317                    else {
318                            return false;
319                    }
320            }
321    
322            protected boolean isPropertyDBCP(String key) {
323                    if (key.equalsIgnoreCase("defaultTransactionIsolation") ||
324                            key.equalsIgnoreCase("maxActive") ||
325                            key.equalsIgnoreCase("minIdle") ||
326                            key.equalsIgnoreCase("removeAbandonedTimeout")) {
327    
328                            return true;
329                    }
330                    else {
331                            return false;
332                    }
333            }
334    
335            protected boolean isPropertyLiferay(String key) {
336                    if (key.equalsIgnoreCase("jndi.name") ||
337                            key.equalsIgnoreCase("liferay.pool.provider")) {
338    
339                            return true;
340                    }
341                    else {
342                            return false;
343                    }
344            }
345    
346            protected boolean isPropertyTomcat(String key) {
347                    if (key.equalsIgnoreCase("fairQueue") ||
348                            key.equalsIgnoreCase("jdbcInterceptors") ||
349                            key.equalsIgnoreCase("jmxEnabled") ||
350                            key.equalsIgnoreCase("timeBetweenEvictionRunsMillis") ||
351                            key.equalsIgnoreCase("useEquals")) {
352    
353                            return true;
354                    }
355                    else {
356                            return false;
357                    }
358            }
359    
360            protected void testClassForName(Properties properties) throws Exception {
361                    String driverClassName = properties.getProperty("driverClassName");
362    
363                    try {
364                            Class.forName(driverClassName);
365                    }
366                    catch (ClassNotFoundException cnfe) {
367                            if (!ServerDetector.isGeronimo() && !ServerDetector.isJetty() &&
368                                    !ServerDetector.isTomcat()) {
369    
370                                    throw cnfe;
371                            }
372    
373                            String url = PropsUtil.get(
374                                    PropsKeys.SETUP_DATABASE_JAR_URL, new Filter(driverClassName));
375                            String name = PropsUtil.get(
376                                    PropsKeys.SETUP_DATABASE_JAR_NAME, new Filter(driverClassName));
377    
378                            if (Validator.isNull(url) || Validator.isNull(name)) {
379                                    throw cnfe;
380                            }
381    
382                            if (HttpUtil.getHttp() == null) {
383                                    HttpUtil httpUtil = new HttpUtil();
384    
385                                    httpUtil.setHttp(new HttpImpl());
386                            }
387    
388                            if (FileUtil.getFile() == null) {
389                                    FileUtil fileUtil = new FileUtil();
390    
391                                    fileUtil.setFile(new FileImpl());
392                            }
393    
394                            JarUtil.downloadAndInstallJar(true, url, name, null);
395                    }
396            }
397    
398            private static final String _TOMCAT_JDBC_POOL_OBJECT_NAME_PREFIX =
399                    "TomcatJDBCPool:type=ConnectionPool,name=";
400    
401            private static Log _log = LogFactoryUtil.getLog(
402                    DataSourceFactoryImpl.class);
403    
404            private static PACL _pacl = new NoPACL();
405    
406            private static class NoPACL implements PACL {
407    
408                    @Override
409                    public DataSource getDataSource(DataSource dataSource) {
410                            return dataSource;
411                    }
412    
413            }
414    
415            public static interface PACL {
416    
417                    public DataSource getDataSource(DataSource dataSource);
418    
419            }
420    
421    }