/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.openjpa.jdbc.schema; import java.security.AccessController; import java.sql.Connection; import java.sql.Driver; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import javax.sql.DataSource; import org.apache.openjpa.lib.util.StringUtil; import org.apache.openjpa.jdbc.conf.JDBCConfiguration; import org.apache.openjpa.jdbc.sql.DBDictionary; import org.apache.openjpa.lib.conf.Configurations; import org.apache.openjpa.lib.jdbc.ConfiguringConnectionDecorator; import org.apache.openjpa.lib.jdbc.ConnectionDecorator; import org.apache.openjpa.lib.jdbc.DecoratingDataSource; import org.apache.openjpa.lib.jdbc.DelegatingDataSource; import org.apache.openjpa.lib.jdbc.JDBCEventConnectionDecorator; import org.apache.openjpa.lib.jdbc.JDBCListener; import org.apache.openjpa.lib.jdbc.LoggingConnectionDecorator; import org.apache.openjpa.lib.log.Log; import org.apache.openjpa.lib.util.J2DoPrivHelper; import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.lib.util.Options; import org.apache.openjpa.util.ImplHelper; import org.apache.openjpa.util.OpenJPAException; import org.apache.openjpa.util.UserException; /** * Factory for {@link DataSource} objects. The factory uses the supplied * configuration to obtain a 3rd-party datasource or to create one, and * to setup prepared statement caching. * * @author Abe White */ public class DataSourceFactory { private static final Localizer _loc = Localizer.forPackage(DataSourceFactory.class); protected static Localizer _eloc = Localizer.forPackage(DelegatingDataSource.class); /** * Create a datasource using the given configuration. */ public static DataSource newDataSource(JDBCConfiguration conf, boolean factory2) { String driver = (factory2) ? conf.getConnection2DriverName() : conf.getConnectionDriverName(); if (StringUtil.isEmpty(driver)) throw new UserException(_loc.get("no-driver", conf)). setFatal(true); ClassLoader loader = conf.getClassResolverInstance(). getClassLoader(DataSourceFactory.class, null); String props = (factory2) ? conf.getConnection2Properties() : conf.getConnectionProperties(); try { Class<?> driverClass; try { driverClass = Class.forName(driver, true, loader); } catch (ClassNotFoundException cnfe) { // try with the core class loader driverClass = Class.forName(driver); } if (Driver.class.isAssignableFrom(driverClass)) { DriverDataSource ds = conf.newDriverDataSourceInstance(); ds.setClassLoader(loader); ds.setConnectionDriverName(driver); ds.setConnectionProperties(Configurations. parseProperties(props)); if (!factory2) { ds.setConnectionFactoryProperties(Configurations. parseProperties(conf.getConnectionFactoryProperties())); ds.setConnectionURL(conf.getConnectionURL()); ds.setConnectionUserName(conf.getConnectionUserName()); ds.setConnectionPassword(conf.getConnectionPassword()); } else { ds.setConnectionFactoryProperties (Configurations.parseProperties(conf. getConnectionFactory2Properties())); ds.setConnectionURL(conf.getConnection2URL()); ds.setConnectionUserName(conf.getConnection2UserName()); ds.setConnectionPassword(conf.getConnection2Password()); } return ds; } // see if their driver name is actually a data source if (DataSource.class.isAssignableFrom(driverClass)) { return (DataSource) Configurations.newInstance(driver, conf, props, AccessController.doPrivileged( J2DoPrivHelper.getClassLoaderAction( DataSource.class))); } } catch (OpenJPAException ke) { throw ke; } catch (Exception e) { throw newConnectException(conf, factory2, e); } // not a driver or a data source; die throw new UserException(_loc.get("bad-driver", driver)).setFatal(true); } /** * Install listeners and base decorators. */ public static DecoratingDataSource decorateDataSource(DataSource ds, JDBCConfiguration conf, boolean factory2) { Options opts = Configurations.parseProperties((factory2) ? conf.getConnectionFactory2Properties() : conf.getConnectionFactoryProperties()); Log jdbcLog = conf.getLog(JDBCConfiguration.LOG_JDBC); Log sqlLog = conf.getLog(JDBCConfiguration.LOG_SQL); DecoratingDataSource dds = new DecoratingDataSource(ds); try { // add user-defined decorators List<ConnectionDecorator> decorators = new ArrayList<ConnectionDecorator>(); decorators.addAll(Arrays.asList(conf. getConnectionDecoratorInstances())); // add jdbc events decorator JDBCEventConnectionDecorator ecd = new JDBCEventConnectionDecorator(); Configurations.configureInstance(ecd, conf, opts); JDBCListener[] listeners = conf.getJDBCListenerInstances(); for (int i = 0; i < listeners.length; i++) ecd.addListener(listeners[i]); decorators.add(ecd); // ask the DriverDataSource to provide any additional decorators if (ds instanceof DriverDataSource) { List<ConnectionDecorator> decs = ((DriverDataSource) ds). createConnectionDecorators(); if (decs != null) decorators.addAll(decs); } // logging decorator LoggingConnectionDecorator lcd = new LoggingConnectionDecorator(); Configurations.configureInstance(lcd, conf, opts); lcd.getLogs().setJDBCLog(jdbcLog); lcd.getLogs().setSQLLog(sqlLog); decorators.add(lcd); dds.addDecorators(decorators); return dds; } catch (OpenJPAException ke) { throw ke; } catch (Exception e) { throw newConnectException(conf, factory2, e); } } /** * Install things deferred until the DBDictionary instance is available. * * @author Steve Kim */ public static DecoratingDataSource installDBDictionary(DBDictionary dict, DecoratingDataSource ds, final JDBCConfiguration conf, boolean factory2) { DataSource inner = ds.getInnermostDelegate(); if (inner instanceof DriverDataSource) ((DriverDataSource) inner).initDBDictionary(dict); Connection conn = null; try { // add the dictionary as a warning handler on the logging decorator ConnectionDecorator cd; for (Iterator<ConnectionDecorator> itr = ds.getDecorators().iterator(); itr.hasNext();) { cd = itr.next(); if (cd instanceof LoggingConnectionDecorator) ((LoggingConnectionDecorator) cd).setWarningHandler(dict); } // misc configuration connection decorator (statement timeouts, // transaction isolation, etc) ConfiguringConnectionDecorator ccd = new ConfiguringConnectionDecorator(); ccd.setTransactionIsolation(conf.getTransactionIsolationConstant()); //OPENJPA-2517: Allow a javax.persistence.query.timeout to apply to all //EM operations (not just Query operations). Convert from milliseconds //to seconds. See DBDictionary.setQueryTimeout for similar conversions. //DBDictionary.setQueryTimeout will log warnings for invalid values, //therefore there is no need to do so again here. Furthermore, there is no //need to check for -1 here, ConfigurationConnectionDecorator checks for it. int timeout = conf.getQueryTimeout(); if (dict.allowQueryTimeoutOnFindUpdate){ if (timeout > 0 && timeout < 1000) { // round up to 1 sec timeout = 1; } else if (timeout >= 1000){ timeout = timeout/1000; } } ccd.setQueryTimeout(timeout); Log log = conf.getLog(JDBCConfiguration.LOG_JDBC); if (factory2 || !conf.isConnectionFactoryModeManaged()) { if (!dict.supportsMultipleNontransactionalResultSets) ccd.setAutoCommit(Boolean.FALSE); else ccd.setAutoCommit(Boolean.TRUE); // add trace info for autoCommit setting if (log.isTraceEnabled()) log.trace(_loc.get("set-auto-commit", new Object[] { dict.supportsMultipleNontransactionalResultSets})); } Options opts = Configurations.parseProperties((factory2) ? conf.getConnectionFactory2Properties() : conf.getConnectionFactoryProperties()); Configurations.configureInstance(ccd, conf, opts); ds.addDecorator(ccd); // allow the dbdictionary to decorate the connection further ds.addDecorator(dict); // ensure dbdictionary to process connectedConfiguration() if (!factory2) conn = ds.getConnection(conf.getConnectionUserName(), conf .getConnectionPassword()); else conn = ds.getConnection(conf.getConnection2UserName(), conf .getConnection2Password()); return ds; } catch (Exception e) { throw newConnectException(conf, factory2, e); } finally { if (conn != null) try { conn.close(); } catch (SQLException se) { // ignore any exception since the connection is not going // to be used anyway } } } static OpenJPAException newConnectException(JDBCConfiguration conf, boolean factory2, Exception cause) { return new UserException(_eloc.get("poolds-null", factory2 ? new Object[]{conf.getConnection2DriverName(), conf.getConnection2URL()} : new Object[]{conf.getConnectionDriverName(), conf.getConnectionURL()}), cause).setFatal(true); } /** * Return a data source with the given user name and password * pre-configured as the defaults when {@link DataSource#getConnection} * is called. */ public static DataSource defaultsDataSource(DataSource ds, String user, String pass) { if (user == null && pass == null) return ds; // also check if they are both blank strings if ("".equals(user) && "".equals(pass)) return ds; return new DefaultsDataSource(ds, user, pass); } /** * Close the given data source. */ public static void closeDataSource(DataSource ds) { if (ds instanceof DelegatingDataSource) ds = ((DelegatingDataSource) ds).getInnermostDelegate(); ImplHelper.close(ds); } /** * A data source with pre-configured default user name and password. */ private static class DefaultsDataSource extends DelegatingDataSource { private final String _user; private final String _pass; public DefaultsDataSource(DataSource ds, String user, String pass) { super(ds); _user = user; _pass = pass; } public Connection getConnection() throws SQLException { return super.getConnection(_user, _pass); } public Connection getConnection(String user, String pass) throws SQLException { return super.getConnection(user, pass); } } }