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)
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    
170                    return initDataSource(properties);
171            }
172    
173            protected DataSource initDataSourceC3PO(Properties properties)
174                    throws Exception {
175    
176                    ComboPooledDataSource comboPooledDataSource =
177                            new ComboPooledDataSource();
178    
179                    String identityToken = PwdGenerator.getPassword(PwdGenerator.KEY2, 8);
180    
181                    comboPooledDataSource.setIdentityToken(identityToken);
182    
183                    Enumeration<String> enu =
184                            (Enumeration<String>)properties.propertyNames();
185    
186                    while (enu.hasMoreElements()) {
187                            String key = enu.nextElement();
188                            String value = properties.getProperty(key);
189    
190                            // Map org.apache.commons.dbcp.BasicDataSource to C3PO
191    
192                            if (key.equalsIgnoreCase("driverClassName")) {
193                                    key = "driverClass";
194                            }
195                            else if (key.equalsIgnoreCase("url")) {
196                                    key = "jdbcUrl";
197                            }
198                            else if (key.equalsIgnoreCase("username")) {
199                                    key = "user";
200                            }
201    
202                            // Ignore Liferay properties
203    
204                            if (isPropertyLiferay(key)) {
205                                    continue;
206                            }
207    
208                            // Ignore DBCP properties
209    
210                            if (isPropertyDBCP(key)) {
211                                    continue;
212                            }
213    
214                            // Ignore Tomcat
215    
216                            if (isPropertyTomcat(key)) {
217                                    continue;
218                            }
219    
220                            try {
221                                    BeanUtil.setProperty(comboPooledDataSource, key, value);
222                            }
223                            catch (Exception e) {
224                                    if (_log.isWarnEnabled()) {
225                                            _log.warn(
226                                                    "Property " + key + " is not a valid C3PO property");
227                                    }
228                            }
229                    }
230    
231                    return comboPooledDataSource;
232            }
233    
234            protected DataSource initDataSourceDBCP(Properties properties)
235                    throws Exception {
236    
237                    return BasicDataSourceFactory.createDataSource(properties);
238            }
239    
240            protected DataSource initDataSourceTomcat(Properties properties)
241                    throws Exception {
242    
243                    PoolProperties poolProperties = new PoolProperties();
244    
245                    for (Map.Entry<Object, Object> entry : properties.entrySet()) {
246                            String key = (String)entry.getKey();
247                            String value = (String)entry.getValue();
248    
249                            // Ignore Liferay properties
250    
251                            if (isPropertyLiferay(key)) {
252                                    continue;
253                            }
254    
255                            // Ignore C3P0 properties
256    
257                            if (isPropertyC3PO(key)) {
258                                    continue;
259                            }
260    
261                            try {
262                                    BeanUtil.setProperty(poolProperties, key, value);
263                            }
264                            catch (Exception e) {
265                                    if (_log.isWarnEnabled()) {
266                                            _log.warn(
267                                                    "Property " + key + " is not a valid Tomcat JDBC " +
268                                                            "Connection Pool property");
269                                    }
270                            }
271                    }
272    
273                    String poolName = PwdGenerator.getPassword(PwdGenerator.KEY2, 8);
274    
275                    poolProperties.setName(poolName);
276    
277                    org.apache.tomcat.jdbc.pool.DataSource dataSource =
278                            new org.apache.tomcat.jdbc.pool.DataSource(poolProperties);
279    
280                    if (poolProperties.isJmxEnabled()) {
281                            org.apache.tomcat.jdbc.pool.ConnectionPool jdbcConnectionPool =
282                                    dataSource.createPool();
283    
284                            ConnectionPool jmxConnectionPool = jdbcConnectionPool.getJmxPool();
285    
286                            MBeanServer mBeanServer =
287                                    ManagementFactory.getPlatformMBeanServer();
288    
289                            ObjectName objectName = new ObjectName(
290                                    _TOMCAT_JDBC_POOL_OBJECT_NAME_PREFIX + poolName);
291    
292                            mBeanServer.registerMBean(jmxConnectionPool, objectName);
293                    }
294    
295                    return dataSource;
296            }
297    
298            protected boolean isPropertyC3PO(String key) {
299                    if (key.equalsIgnoreCase("acquireIncrement") ||
300                            key.equalsIgnoreCase("acquireRetryAttempts") ||
301                            key.equalsIgnoreCase("acquireRetryDelay") ||
302                            key.equalsIgnoreCase("connectionCustomizerClassName") ||
303                            key.equalsIgnoreCase("idleConnectionTestPeriod") ||
304                            key.equalsIgnoreCase("maxIdleTime") ||
305                            key.equalsIgnoreCase("maxPoolSize") ||
306                            key.equalsIgnoreCase("minPoolSize") ||
307                            key.equalsIgnoreCase("numHelperThreads") ||
308                            key.equalsIgnoreCase("preferredTestQuery")) {
309    
310                            return true;
311                    }
312                    else {
313                            return false;
314                    }
315            }
316    
317            protected boolean isPropertyDBCP(String key) {
318                    if (key.equalsIgnoreCase("defaultTransactionIsolation") ||
319                            key.equalsIgnoreCase("maxActive") ||
320                            key.equalsIgnoreCase("minIdle") ||
321                            key.equalsIgnoreCase("removeAbandonedTimeout")) {
322    
323                            return true;
324                    }
325                    else {
326                            return false;
327                    }
328            }
329    
330            protected boolean isPropertyLiferay(String key) {
331                    if (key.equalsIgnoreCase("jndi.name") ||
332                            key.equalsIgnoreCase("liferay.pool.provider")) {
333    
334                            return true;
335                    }
336                    else {
337                            return false;
338                    }
339            }
340    
341            protected boolean isPropertyTomcat(String key) {
342                    if (key.equalsIgnoreCase("fairQueue") ||
343                            key.equalsIgnoreCase("jdbcInterceptors") ||
344                            key.equalsIgnoreCase("jmxEnabled") ||
345                            key.equalsIgnoreCase("timeBetweenEvictionRunsMillis") ||
346                            key.equalsIgnoreCase("useEquals")) {
347    
348                            return true;
349                    }
350                    else {
351                            return false;
352                    }
353            }
354    
355            protected void testClassForName(Properties properties) throws Exception {
356                    String driverClassName = properties.getProperty("driverClassName");
357    
358                    try {
359                            Class.forName(driverClassName);
360                    }
361                    catch (ClassNotFoundException cnfe) {
362                            if (!ServerDetector.isGeronimo() && !ServerDetector.isJetty() &&
363                                    !ServerDetector.isTomcat()) {
364    
365                                    throw cnfe;
366                            }
367    
368                            String url = PropsUtil.get(
369                                    PropsKeys.SETUP_DATABASE_JAR_URL, new Filter(driverClassName));
370                            String name = PropsUtil.get(
371                                    PropsKeys.SETUP_DATABASE_JAR_NAME, new Filter(driverClassName));
372    
373                            if (Validator.isNull(url) || Validator.isNull(name)) {
374                                    throw cnfe;
375                            }
376    
377                            if (HttpUtil.getHttp() == null) {
378                                    HttpUtil httpUtil = new HttpUtil();
379    
380                                    httpUtil.setHttp(new HttpImpl());
381                            }
382    
383                            if (FileUtil.getFile() == null) {
384                                    FileUtil fileUtil = new FileUtil();
385    
386                                    fileUtil.setFile(new FileImpl());
387                            }
388    
389                            JarUtil.downloadAndInstallJar(true, url, name, null);
390                    }
391            }
392    
393            private static final String _TOMCAT_JDBC_POOL_OBJECT_NAME_PREFIX =
394                    "TomcatJDBCPool:type=ConnectionPool,name=";
395    
396            private static Log _log = LogFactoryUtil.getLog(
397                    DataSourceFactoryImpl.class);
398    
399    }