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