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