001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.openjpa.jdbc.schema;
020:
021: import java.security.AccessController;
022: import java.sql.Connection;
023: import java.sql.Driver;
024: import java.sql.SQLException;
025: import java.util.ArrayList;
026: import java.util.Arrays;
027: import java.util.Iterator;
028: import java.util.List;
029: import javax.sql.DataSource;
030:
031: import org.apache.commons.lang.StringUtils;
032: import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
033: import org.apache.openjpa.jdbc.sql.DBDictionary;
034: import org.apache.openjpa.lib.conf.Configurations;
035: import org.apache.openjpa.lib.jdbc.ConfiguringConnectionDecorator;
036: import org.apache.openjpa.lib.jdbc.ConnectionDecorator;
037: import org.apache.openjpa.lib.jdbc.DecoratingDataSource;
038: import org.apache.openjpa.lib.jdbc.DelegatingDataSource;
039: import org.apache.openjpa.lib.jdbc.JDBCEventConnectionDecorator;
040: import org.apache.openjpa.lib.jdbc.JDBCListener;
041: import org.apache.openjpa.lib.jdbc.LoggingConnectionDecorator;
042: import org.apache.openjpa.lib.log.Log;
043: import org.apache.openjpa.lib.util.J2DoPrivHelper;
044: import org.apache.openjpa.lib.util.Localizer;
045: import org.apache.openjpa.lib.util.Options;
046: import org.apache.openjpa.util.ImplHelper;
047: import org.apache.openjpa.util.OpenJPAException;
048: import org.apache.openjpa.util.StoreException;
049: import org.apache.openjpa.util.UserException;
050:
051: /**
052: * Factory for {@link DataSource} objects. The factory uses the supplied
053: * configuration to obtain a 3rd-party datasource or to create one, and
054: * to setup prepared statement caching.
055: *
056: * @author Abe White
057: * @nojavadoc
058: */
059: public class DataSourceFactory {
060:
061: private static final Localizer _loc = Localizer
062: .forPackage(DataSourceFactory.class);
063:
064: /**
065: * Create a datasource using the given configuration.
066: */
067: public static DataSource newDataSource(JDBCConfiguration conf,
068: boolean factory2) {
069: String driver = (factory2) ? conf.getConnection2DriverName()
070: : conf.getConnectionDriverName();
071: if (StringUtils.isEmpty(driver))
072: throw new UserException(_loc.get("no-driver", driver))
073: .setFatal(true);
074:
075: ClassLoader loader = conf.getClassResolverInstance()
076: .getClassLoader(DataSourceFactory.class, null);
077: String props = (factory2) ? conf.getConnection2Properties()
078: : conf.getConnectionProperties();
079: try {
080: Class driverClass;
081: try {
082: driverClass = Class.forName(driver, true, loader);
083: } catch (ClassNotFoundException cnfe) {
084: // try with the core class loader
085: driverClass = Class.forName(driver);
086: }
087:
088: if (Driver.class.isAssignableFrom(driverClass)) {
089: DriverDataSource ds = conf
090: .newDriverDataSourceInstance();
091: ds.setClassLoader(loader);
092: ds.setConnectionDriverName(driver);
093: ds.setConnectionProperties(Configurations
094: .parseProperties(props));
095:
096: if (!factory2) {
097: ds.setConnectionFactoryProperties(Configurations
098: .parseProperties(conf
099: .getConnectionFactoryProperties()));
100: ds.setConnectionURL(conf.getConnectionURL());
101: ds.setConnectionUserName(conf
102: .getConnectionUserName());
103: ds.setConnectionPassword(conf
104: .getConnectionPassword());
105: } else {
106: ds
107: .setConnectionFactoryProperties(Configurations
108: .parseProperties(conf
109: .getConnectionFactory2Properties()));
110: ds.setConnectionURL(conf.getConnection2URL());
111: ds.setConnectionUserName(conf
112: .getConnection2UserName());
113: ds.setConnectionPassword(conf
114: .getConnection2Password());
115: }
116: return ds;
117: }
118:
119: // see if their driver name is actually a data source
120: if (DataSource.class.isAssignableFrom(driverClass)) {
121: return (DataSource) Configurations
122: .newInstance(
123: driver,
124: conf,
125: props,
126: (ClassLoader) AccessController
127: .doPrivileged(J2DoPrivHelper
128: .getClassLoaderAction(DataSource.class)));
129: }
130: } catch (OpenJPAException ke) {
131: throw ke;
132: } catch (Exception e) {
133: throw new StoreException(e).setFatal(true);
134: }
135:
136: // not a driver or a data source; die
137: throw new UserException(_loc.get("bad-driver", driver))
138: .setFatal(true);
139: }
140:
141: /**
142: * Install listeners and base decorators.
143: */
144: public static DecoratingDataSource decorateDataSource(
145: DataSource ds, JDBCConfiguration conf, boolean factory2) {
146: Options opts = Configurations.parseProperties((factory2) ? conf
147: .getConnectionFactory2Properties() : conf
148: .getConnectionFactoryProperties());
149: Log jdbcLog = conf.getLog(JDBCConfiguration.LOG_JDBC);
150: Log sqlLog = conf.getLog(JDBCConfiguration.LOG_SQL);
151:
152: DecoratingDataSource dds = new DecoratingDataSource(ds);
153: try {
154: // add user-defined decorators
155: List decorators = new ArrayList();
156: decorators.addAll(Arrays.asList(conf
157: .getConnectionDecoratorInstances()));
158:
159: // add jdbc events decorator
160: JDBCEventConnectionDecorator ecd = new JDBCEventConnectionDecorator();
161: Configurations.configureInstance(ecd, conf, opts);
162: JDBCListener[] listeners = conf.getJDBCListenerInstances();
163: for (int i = 0; i < listeners.length; i++)
164: ecd.addListener(listeners[i]);
165: decorators.add(ecd);
166:
167: // ask the DriverDataSource to provide any additional decorators
168: if (ds instanceof DriverDataSource) {
169: List decs = ((DriverDataSource) ds)
170: .createConnectionDecorators();
171: if (decs != null)
172: decorators.addAll(decs);
173: }
174:
175: if (jdbcLog.isTraceEnabled() || sqlLog.isTraceEnabled()) {
176: // logging decorator
177: LoggingConnectionDecorator lcd = new LoggingConnectionDecorator();
178: Configurations.configureInstance(lcd, conf, opts);
179: lcd.getLogs().setJDBCLog(jdbcLog);
180: lcd.getLogs().setSQLLog(sqlLog);
181: decorators.add(lcd);
182: }
183:
184: dds.addDecorators(decorators);
185: return dds;
186: } catch (OpenJPAException ke) {
187: throw ke;
188: } catch (Exception e) {
189: throw new StoreException(e).setFatal(true);
190: }
191: }
192:
193: /**
194: * Install things deferred until the DBDictionary instance is available.
195: *
196: * @author Steve Kim
197: */
198: public static DecoratingDataSource installDBDictionary(
199: DBDictionary dict, DecoratingDataSource ds,
200: final JDBCConfiguration conf, boolean factory2) {
201: DataSource inner = ds.getInnermostDelegate();
202: if (inner instanceof DriverDataSource)
203: ((DriverDataSource) inner).initDBDictionary(dict);
204: Connection conn = null;
205:
206: try {
207: // add the dictionary as a warning handler on the logging
208: // decorator
209: ConnectionDecorator cd;
210: for (Iterator itr = ds.getDecorators().iterator(); itr
211: .hasNext();) {
212: cd = (ConnectionDecorator) itr.next();
213: if (cd instanceof LoggingConnectionDecorator)
214: ((LoggingConnectionDecorator) cd)
215: .setWarningHandler(dict);
216: }
217:
218: // misc configuration connection decorator (statement timeouts,
219: // transaction isolation, etc)
220: ConfiguringConnectionDecorator ccd = new ConfiguringConnectionDecorator();
221: ccd.setTransactionIsolation(conf
222: .getTransactionIsolationConstant());
223: if (factory2 || !conf.isConnectionFactoryModeManaged()) {
224: if (!dict.supportsMultipleNontransactionalResultSets)
225: ccd.setAutoCommit(Boolean.FALSE);
226: else
227: ccd.setAutoCommit(Boolean.TRUE);
228: }
229: Options opts = Configurations
230: .parseProperties((factory2) ? conf
231: .getConnectionFactory2Properties() : conf
232: .getConnectionFactoryProperties());
233: Configurations.configureInstance(ccd, conf, opts);
234: ds.addDecorator(ccd);
235:
236: // allow the dbdictionary to decorate the connection further
237: ds.addDecorator(dict);
238:
239: // ensure dbdictionary to process connectedConfiguration()
240: if (!factory2)
241: conn = ds.getConnection(conf.getConnectionUserName(),
242: conf.getConnectionPassword());
243: else
244: conn = ds.getConnection(conf.getConnection2UserName(),
245: conf.getConnection2Password());
246:
247: return ds;
248: } catch (Exception e) {
249: throw new StoreException(e).setFatal(true);
250: } finally {
251: if (conn != null)
252: try {
253: conn.close();
254: } catch (SQLException se) {
255: // ignore any exception since the connection is not going
256: // to be used anyway
257: }
258: }
259: }
260:
261: /**
262: * Return a data source with the given user name and password
263: * pre-configured as the defaults when {@link DataSource#getConnection}
264: * is called.
265: */
266: public static DataSource defaultsDataSource(DataSource ds,
267: String user, String pass) {
268: if (user == null && pass == null)
269: return ds;
270: // also check if they are both blank strings
271: if ("".equals(user) && "".equals(pass))
272: return ds;
273: return new DefaultsDataSource(ds, user, pass);
274: }
275:
276: /**
277: * Close the given data source.
278: */
279: public static void closeDataSource(DataSource ds) {
280: if (ds instanceof DelegatingDataSource)
281: ds = ((DelegatingDataSource) ds).getInnermostDelegate();
282: ImplHelper.close(ds);
283: }
284:
285: /**
286: * A data source with pre-configured default user name and password.
287: */
288: private static class DefaultsDataSource extends
289: DelegatingDataSource {
290:
291: private final String _user;
292: private final String _pass;
293:
294: public DefaultsDataSource(DataSource ds, String user,
295: String pass) {
296: super (ds);
297: _user = user;
298: _pass = pass;
299: }
300:
301: public Connection getConnection() throws SQLException {
302: return super .getConnection(_user, _pass);
303: }
304:
305: public Connection getConnection(String user, String pass)
306: throws SQLException {
307: return super.getConnection(user, pass);
308: }
309: }
310: }
|