001: package org.apache.ojb.broker.accesslayer;
002:
003: /* Copyright 2002-2005 The Apache Software Foundation
004: *
005: * Licensed under the Apache License, Version 2.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
019: import org.apache.ojb.broker.platforms.PlatformException;
020: import org.apache.ojb.broker.platforms.PlatformFactory;
021: import org.apache.ojb.broker.util.ClassHelper;
022: import org.apache.ojb.broker.util.logging.Logger;
023: import org.apache.ojb.broker.util.logging.LoggerFactory;
024:
025: import javax.naming.InitialContext;
026: import javax.naming.NamingException;
027: import javax.sql.DataSource;
028: import java.sql.Connection;
029: import java.sql.DriverManager;
030: import java.sql.SQLException;
031: import java.util.HashMap;
032: import java.util.Map;
033: import java.util.Properties;
034:
035: /**
036: * Abstract base class to simplify implementation of {@link ConnectionFactory}'s.
037: *
038: * @author <a href="mailto:armin@codeAuLait.de">Armin Waibel</a>
039: * @version $Id: ConnectionFactoryAbstractImpl.java,v 1.10.2.5 2005/04/30 20:55:15 mkalen Exp $
040: */
041: public abstract class ConnectionFactoryAbstractImpl implements
042: ConnectionFactory {
043: private Logger log = LoggerFactory
044: .getLogger(ConnectionFactoryAbstractImpl.class);
045:
046: /**
047: * holds the datasource looked up from JNDI in a map, keyed
048: * by the JNDI name.
049: */
050: private Map dataSourceCache = new HashMap();
051:
052: /**
053: * Returns a valid JDBC Connection. Implement this method in concrete subclasses.
054: * Concrete implementations using Connection pooling are responsible for any validation
055: * and pool removal management.
056: * <p>
057: * Note: This method is never called for a jdbc-connection-descriptor that uses datasources,
058: * OJB only manages connections from DriverManager.
059: * <p>
060: * Note: If the concrete implementation does not callback to
061: * {@link #newConnectionFromDriverManager(org.apache.ojb.broker.metadata.JdbcConnectionDescriptor)}
062: * when creating a new Connection, it <em>must</em> call
063: * {@link #initializeJdbcConnection(java.sql.Connection, org.apache.ojb.broker.metadata.JdbcConnectionDescriptor)}
064: * so that the platform implementation can peform any RDBMS-specific init tasks for newly
065: * created Connection objetcs.
066: *
067: * @param jcd the connection descriptor for which to return a validated Connection
068: * @return a valid Connection, never null.
069: * Specific implementations <em>must</em> guarantee that the connection is not null and
070: * that it is valid.
071: * @throws LookupException if a valid Connection could not be obtained
072: */
073: public abstract Connection checkOutJdbcConnection(
074: JdbcConnectionDescriptor jcd) throws LookupException;
075:
076: /**
077: * Releases a Connection after use. Implement this method in concrete subclasses.
078: * Concrete implementations using Connection pooling are responsible for any validation
079: * and pool removal management.
080: * <p>
081: * Note: This method is never called for a jdbc-connection-descriptor that uses datasources,
082: * OJB only manages connections from DriverManager.
083: *
084: * @param jcd the connection descriptor for which the connection was created
085: * @param con the connection to release.
086: * Callers <em>must</em> guarantee that the passed connection was obtained by calling
087: * {@link #checkOutJdbcConnection(org.apache.ojb.broker.metadata.JdbcConnectionDescriptor)}.
088: * @throws LookupException if errors occured during release of object. Typically happens
089: * if return of object to pool fails in a pooled implementation.
090: */
091: public abstract void releaseJdbcConnection(
092: JdbcConnectionDescriptor jcd, Connection con)
093: throws LookupException;
094:
095: public void releaseConnection(JdbcConnectionDescriptor jcd,
096: Connection con) {
097: if (con == null)
098: return;
099: if (jcd.isDataSource()) {
100: try {
101: con.close();
102: } catch (SQLException e) {
103: log.error("Closing connection failed", e);
104: }
105: } else {
106: try {
107: releaseJdbcConnection(jcd, con);
108: } catch (LookupException e) {
109: log.error(
110: "Unexpected exception when return connection "
111: + con + " to pool using " + jcd, e);
112: }
113: }
114: }
115:
116: public Connection lookupConnection(JdbcConnectionDescriptor jcd)
117: throws LookupException {
118: Connection conn;
119: /*
120: use JNDI datasourcelookup or ordinary jdbc DriverManager
121: to obtain connection ?
122: */
123: if (jcd.isDataSource()) {
124: if (log.isDebugEnabled()) {
125: log.debug("do datasource lookup, name: "
126: + jcd.getDatasourceName() + ", user: "
127: + jcd.getUserName());
128: }
129: conn = newConnectionFromDataSource(jcd);
130: } else {
131: conn = checkOutJdbcConnection(jcd);
132: // connection is now guaranteed to be valid by API contract (else exception is thrown)
133: }
134: return conn;
135: }
136:
137: /**
138: * Initialize the connection with the specified properties in OJB
139: * configuration files and platform depended properties.
140: * Invoke this method after a NEW connection is created, not if re-using from pool.
141: *
142: * @see org.apache.ojb.broker.platforms.PlatformFactory
143: * @see org.apache.ojb.broker.platforms.Platform
144: */
145: protected void initializeJdbcConnection(Connection con,
146: JdbcConnectionDescriptor jcd) throws LookupException {
147: try {
148: PlatformFactory.getPlatformFor(jcd)
149: .initializeJdbcConnection(jcd, con);
150: } catch (PlatformException e) {
151: throw new LookupException(
152: "Platform dependent initialization of connection failed",
153: e);
154: }
155: }
156:
157: /**
158: * Override this method to do cleanup in your implementation.
159: * Do a <tt>super.releaseAllResources()</tt> in your method implementation
160: * to free resources used by this class.
161: */
162: public synchronized void releaseAllResources() {
163: this .dataSourceCache.clear();
164: }
165:
166: /**
167: * Creates a new connection from the data source that the connection descriptor
168: * represents. If the connection descriptor does not directly contain the data source
169: * then a JNDI lookup is performed to retrieve the data source.
170: *
171: * @param jcd The connection descriptor
172: * @return A connection instance
173: * @throws LookupException if we can't get a connection from the datasource either due to a
174: * naming exception, a failed sanity check, or a SQLException.
175: */
176: protected Connection newConnectionFromDataSource(
177: JdbcConnectionDescriptor jcd) throws LookupException {
178: Connection retval = null;
179: // use JNDI lookup
180: DataSource ds = jcd.getDataSource();
181:
182: if (ds == null) {
183: // [tomdz] Would it suffice to store the datasources only at the JCDs ?
184: // Only possible problem would be serialization of the JCD because
185: // the data source object in the JCD does not 'survive' this
186: ds = (DataSource) dataSourceCache.get(jcd
187: .getDatasourceName());
188: }
189: try {
190: if (ds == null) {
191: /**
192: * this synchronization block won't be a big deal as we only look up
193: * new datasources not found in the map.
194: */
195: synchronized (dataSourceCache) {
196: InitialContext ic = new InitialContext();
197: ds = (DataSource) ic
198: .lookup(jcd.getDatasourceName());
199: /**
200: * cache the datasource lookup.
201: */
202: dataSourceCache.put(jcd.getDatasourceName(), ds);
203: }
204: }
205: if (jcd.getUserName() == null) {
206: retval = ds.getConnection();
207: } else {
208: retval = ds.getConnection(jcd.getUserName(), jcd
209: .getPassWord());
210: }
211: } catch (SQLException sqlEx) {
212: log.error(
213: "SQLException thrown while trying to get Connection from Datasource ("
214: + jcd.getDatasourceName() + ")", sqlEx);
215: throw new LookupException(
216: "SQLException thrown while trying to get Connection from Datasource ("
217: + jcd.getDatasourceName() + ")", sqlEx);
218: } catch (NamingException namingEx) {
219: log.error("Naming Exception while looking up DataSource ("
220: + jcd.getDatasourceName() + ")", namingEx);
221: throw new LookupException(
222: "Naming Exception while looking up DataSource ("
223: + jcd.getDatasourceName() + ")", namingEx);
224: }
225: // initialize connection
226: initializeJdbcConnection(retval, jcd);
227: if (log.isDebugEnabled())
228: log.debug("Create new connection using DataSource: "
229: + retval);
230: return retval;
231: }
232:
233: /**
234: * Returns a new created connection
235: *
236: * @param jcd the connection descriptor
237: * @return an instance of Connection from the drivermanager
238: */
239: protected Connection newConnectionFromDriverManager(
240: JdbcConnectionDescriptor jcd) throws LookupException {
241: Connection retval = null;
242: // use JDBC DriverManager
243: final String driver = jcd.getDriver();
244: final String url = getDbURL(jcd);
245: try {
246: // loads the driver - NB call to newInstance() added to force initialisation
247: ClassHelper.getClass(driver, true);
248: final String user = jcd.getUserName();
249: final String password = jcd.getPassWord();
250: final Properties properties = getJdbcProperties(jcd, user,
251: password);
252: if (properties.isEmpty()) {
253: if (user == null) {
254: retval = DriverManager.getConnection(url);
255: } else {
256: retval = DriverManager.getConnection(url, user,
257: password);
258: }
259: } else {
260: retval = DriverManager.getConnection(url, properties);
261: }
262: } catch (SQLException sqlEx) {
263: log.error(
264: "Error getting Connection from DriverManager with url ("
265: + url + ") and driver (" + driver + ")",
266: sqlEx);
267: throw new LookupException(
268: "Error getting Connection from DriverManager with url ("
269: + url + ") and driver (" + driver + ")",
270: sqlEx);
271: } catch (ClassNotFoundException cnfEx) {
272: log.error(cnfEx);
273: throw new LookupException("A class was not found", cnfEx);
274: } catch (Exception e) {
275: log.error("Instantiation of jdbc driver failed", e);
276: throw new LookupException(
277: "Instantiation of jdbc driver failed", e);
278: }
279: // initialize connection
280: initializeJdbcConnection(retval, jcd);
281: if (log.isDebugEnabled())
282: log.debug("Create new connection using DriverManager: "
283: + retval);
284: return retval;
285: }
286:
287: /**
288: * Returns connection properties for passing to DriverManager, after merging
289: * JDBC driver-specific configuration settings with name/password from connection
290: * descriptor.
291: * @param jcd the connection descriptor with driver-specific settings
292: * @param user the jcd username (or null if not using authenticated login)
293: * @param password the jcd password (only used when user != null)
294: * @return merged properties object to pass to DriverManager
295: */
296: protected Properties getJdbcProperties(
297: JdbcConnectionDescriptor jcd, String user, String password) {
298: final Properties jdbcProperties;
299: jdbcProperties = jcd.getConnectionPoolDescriptor()
300: .getJdbcProperties();
301: if (user != null) {
302: jdbcProperties.put("user", user);
303: jdbcProperties.put("password", password);
304: }
305: return jdbcProperties;
306: }
307:
308: protected Properties getJdbcProperties(JdbcConnectionDescriptor jcd) {
309: final String user = jcd.getUserName();
310: final String password = jcd.getPassWord();
311: return getJdbcProperties(jcd, user, password);
312: }
313:
314: protected String getDbURL(JdbcConnectionDescriptor jcd) {
315: return jcd.isDataSource() ? jcd.getDatasourceName() : jcd
316: .getProtocol()
317: + ":" + jcd.getSubProtocol() + ":" + jcd.getDbAlias();
318: }
319:
320: protected String getJcdDescription(JdbcConnectionDescriptor jcd) {
321: return "Connection for JdbcConnectionDescriptor ("
322: + (jcd.getDatasourceName() != null ? "datasource: "
323: + jcd.getDatasourceName() : "db-url: "
324: + getDbURL(jcd) + ", user: "
325: + jcd.getUserName()) + ")";
326: }
327:
328: }
|