001: /*
002: * This software is released under a licence similar to the Apache Software Licence.
003: * See org.logicalcobwebs.proxool.package.html for details.
004: * The latest version is available at http://proxool.sourceforge.net
005: */
006: package org.logicalcobwebs.proxool;
007:
008: import org.logicalcobwebs.cglib.proxy.Enhancer;
009: import org.logicalcobwebs.cglib.proxy.Factory;
010: import org.logicalcobwebs.cglib.proxy.Callback;
011: import org.logicalcobwebs.cglib.core.NamingPolicy;
012: import org.logicalcobwebs.cglib.core.Predicate;
013: import org.apache.commons.logging.Log;
014: import org.apache.commons.logging.LogFactory;
015:
016: import java.sql.Connection;
017: import java.sql.DatabaseMetaData;
018: import java.sql.Statement;
019: import java.sql.CallableStatement;
020: import java.sql.PreparedStatement;
021: import java.util.HashSet;
022: import java.util.Set;
023: import java.util.Map;
024: import java.util.HashMap;
025: import java.lang.reflect.Modifier;
026:
027: /**
028: * A central place to build proxy objects. It will also provide the original
029: * object given a proxy.
030: * @author Bill Horsman (bill@logicalcobwebs.co.uk)
031: * @author $Author: billhorsman $ (current maintainer)
032: * @version $Revision: 1.33 $, $Date: 2006/04/09 21:08:43 $
033: * @since Proxool 0.5
034: */
035: class ProxyFactory {
036:
037: private static final Log LOG = LogFactory
038: .getLog(ProxyFactory.class);
039:
040: private static Map interfaceMap = new HashMap();
041:
042: /**
043: * This naming policy stops conflicts with other Cglib instances that are running
044: * (Even ones in different packages). Without using our own naming policy we end
045: * up with exceptions like:
046: * <pre>
047: * java.lang.LinkageError: duplicate class definition:
048: * $java/lang/Object$$FastClassByCGLIB$$3f697993
049: * </pre>
050: */
051: private static NamingPolicy NAMING_POLICY = new NamingPolicy() {
052: public String getClassName(String prefix, String source,
053: Object key, Predicate names) {
054: StringBuffer sb = new StringBuffer();
055: sb
056: .append((prefix != null) ? (prefix
057: .startsWith("java") ? "$" + prefix : prefix)
058: : "net.sf.cglib.empty.Object");
059: sb.append("$$");
060: sb.append(source.substring(source.lastIndexOf('.') + 1));
061: sb.append("ByProxool$$");
062: sb.append(Integer.toHexString(key.hashCode()));
063: String base = sb.toString();
064: String attempt = base;
065: int index = 2;
066: while (names.evaluate(attempt)) {
067: attempt = base + "_" + index++;
068: }
069:
070: return attempt;
071: }
072: };
073:
074: /**
075: * Wraps up a proxyConnection inside a {@link WrappedConnection} and then proxies it as a
076: * simple {@link Connection}. You should call this immediately before the connection is served
077: * to the user. The WrappedConnection is disposable (it is thrown away when the connection
078: * is returned to the pool).
079: * @param proxyConnection the pooled connection we are wrapping up
080: * @return the Connection for use
081: */
082: protected static Connection getWrappedConnection(
083: ProxyConnection proxyConnection) {
084: return (Connection) getProxy(proxyConnection.getConnection(),
085: new WrappedConnection(proxyConnection), proxyConnection
086: .getDefinition());
087: }
088:
089: /**
090: * Proxies a statement inside a {@link ProxyStatement}.
091: * @param delegate the real statement
092: * @param connectionPool the pool it belongs to
093: * @param proxyConnection the connection it was built from
094: * @param sqlStatement Can be null?
095: * @return the proxied statement
096: */
097: protected static Statement getStatement(Statement delegate,
098: ConnectionPool connectionPool,
099: ProxyConnectionIF proxyConnection, String sqlStatement) {
100: return (Statement) getProxy(delegate,
101: new ProxyStatement(delegate, connectionPool,
102: proxyConnection, sqlStatement), proxyConnection
103: .getDefinition());
104: }
105:
106: /**
107: * Create a new DatabaseMetaData from a connection
108: * @param databaseMetaData the meta data we use to delegate all calls to (except getConnection())
109: * @param wrappedConnection the connection we return if asked for the connection
110: * @return databaseMetaData
111: */
112: protected static DatabaseMetaData getDatabaseMetaData(
113: DatabaseMetaData databaseMetaData,
114: Connection wrappedConnection) {
115: return (DatabaseMetaData) getProxy(databaseMetaData,
116: new ProxyDatabaseMetaData(databaseMetaData,
117: wrappedConnection), null);
118: }
119:
120: private static Object getProxy(Object delegate, Callback callback,
121: ConnectionPoolDefinitionIF def) {
122: Enhancer e = new Enhancer();
123: e.setNamingPolicy(NAMING_POLICY);
124: e.setInterfaces(getInterfaces(delegate.getClass(), def));
125: e.setCallback(callback);
126: e.setClassLoader(ProxyFactory.class.getClassLoader());
127: return e.create();
128: }
129:
130: /**
131: * Gets the real Statement that we got from the delegate driver. This is no longer
132: * necessary and only provided for backwards compatability.
133: * @param statement proxy statement
134: * @return delegate statement
135: * @see ProxoolFacade#getDelegateStatement(java.sql.Statement)
136: */
137: protected static Statement getDelegateStatement(Statement statement) {
138: Statement ds = statement;
139: ProxyStatement ps = (ProxyStatement) ((Factory) statement)
140: .getCallback(0);
141: ds = ps.getDelegateStatement();
142: return ds;
143: }
144:
145: /**
146: * Gets the real Connection that we got from the delegate driver. This is no longer
147: * necessary and only provided for backwards compatability.
148: * @param connection proxy connection
149: * @return deletgate connection
150: * @see ProxoolFacade#getDelegateConnection(java.sql.Connection)
151: */
152: protected static Connection getDelegateConnection(
153: Connection connection) {
154: WrappedConnection wc = (WrappedConnection) ((Factory) connection)
155: .getCallback(0);
156: return wc.getProxyConnection().getConnection();
157: }
158:
159: /**
160: * Get all the interfaces that a class implements. Drills down into super interfaces too
161: * and super classes too.
162: * The results are cached so it's very fast second time round.
163: * @param clazz the class to examine.
164: * @return an array of classes (all interfaces) that this class implements.
165: */
166: private static Class[] getInterfaces(Class clazz,
167: ConnectionPoolDefinitionIF cpd) {
168: Class[] interfaceArray = (Class[]) interfaceMap.get(clazz);
169: if (interfaceArray == null) {
170: Set interfaces = new HashSet();
171: traverseInterfacesRecursively(interfaces, clazz);
172: if (cpd != null) {
173: // Work out which interface we should be injecting (if it has been configured). Make sure
174: // we check CallableStatement then PreparedStatement then Statement or all three will get
175: // caught by Statement
176: if (Connection.class.isAssignableFrom(clazz)) {
177: Class injectableClass = cpd
178: .getInjectableConnectionInterface();
179: // Inject it if it was configured.
180: if (injectableClass != null) {
181: interfaces.add(injectableClass);
182: if (LOG.isDebugEnabled()) {
183: LOG.debug("Injecting " + injectableClass
184: + " into " + clazz);
185: }
186: }
187: }
188: if (CallableStatement.class.isAssignableFrom(clazz)) {
189: if (LOG.isDebugEnabled()) {
190: LOG
191: .debug("Getting injectableCallableStatementInterface");
192: }
193: Class injectableClass = cpd
194: .getInjectableCallableStatementInterface();
195: // Inject it if it was configured.
196: if (injectableClass != null) {
197: interfaces.add(injectableClass);
198: if (LOG.isDebugEnabled()) {
199: LOG.debug("Injecting " + injectableClass
200: + " into " + clazz);
201: }
202: }
203: }
204: if (PreparedStatement.class.isAssignableFrom(clazz)) {
205: Class injectableClass = cpd
206: .getInjectablePreparedStatementInterface();
207: // Inject it if it was configured.
208: if (injectableClass != null) {
209: interfaces.add(injectableClass);
210: if (LOG.isDebugEnabled()) {
211: LOG.debug("Injecting " + injectableClass
212: + " into " + clazz);
213: }
214: }
215: }
216: if (Statement.class.isAssignableFrom(clazz)) {
217: Class injectableClass = cpd
218: .getInjectableStatementInterface();
219: // Inject it if it was configured.
220: if (injectableClass != null) {
221: interfaces.add(injectableClass);
222: if (LOG.isDebugEnabled()) {
223: LOG.debug("Injecting " + injectableClass
224: + " into " + clazz);
225: }
226: }
227: }
228: }
229: interfaceArray = (Class[]) interfaces
230: .toArray(new Class[interfaces.size()]);
231: if (LOG.isDebugEnabled()) {
232: for (int i = 0; i < interfaceArray.length; i++) {
233: Class aClass = interfaceArray[i];
234: LOG.debug("Implementing " + aClass);
235: }
236: }
237: interfaceMap.put(clazz, interfaceArray);
238: /*
239: } else {
240: if (LOG.isDebugEnabled()) {
241: LOG.debug("Reusing " + interfaceArray.length + " interfaces already looked up for " + clazz);
242: }
243: */
244: }
245: return interfaceArray;
246: }
247:
248: /**
249: * Recursively looks at all interfaces for a class. Also looks at interfaces implemented
250: * by the super class (and its super class, etc.) Quite a lot of processing involved
251: * so you shouldn't call it too often.
252: * @param interfaces this set is populated with all interfaceMap it finds
253: * @param clazz the base class to analyze
254: */
255: private static void traverseInterfacesRecursively(Set interfaces,
256: Class clazz) {
257: // Check for circular reference (avoid endless recursion)
258: if (interfaces.contains(clazz)) {
259: // Skip it, we've already been here.
260: /*
261: if (LOG.isDebugEnabled()) {
262: LOG.debug("Skipping " + clazz + " because we've already traversed it");
263: }
264: */
265: } else {
266: /*
267: if (LOG.isDebugEnabled()) {
268: LOG.debug("Analyzing " + clazz);
269: }
270: */
271: Class[] interfaceArray = clazz.getInterfaces();
272: for (int i = 0; i < interfaceArray.length; i++) {
273: /*
274: if (LOG.isDebugEnabled()) {
275: LOG.debug("Adding " + interfaceArray[i]);
276: }
277: */
278: traverseInterfacesRecursively(interfaces,
279: interfaceArray[i]);
280: // We're only interested in public interfaces. In fact, including
281: // non-public interfaces will give IllegalAccessExceptions.
282: if (Modifier.isPublic(interfaceArray[i].getModifiers())) {
283: interfaces.add(interfaceArray[i]);
284: }
285: }
286: Class super Clazz = clazz.getSuperclass();
287: if (super Clazz != null) {
288: traverseInterfacesRecursively(interfaces, super Clazz);
289: }
290: /*
291: if (LOG.isDebugEnabled()) {
292: LOG.debug("Found " + interfaceArray.length + " interfaceMap for " + clazz);
293: }
294: */
295: }
296: }
297:
298: /**
299: * Get the WrappedConnection behind this proxy connection.
300: * @param connection the connection that was served
301: * @return the wrapped connection or null if it couldn't be found
302: */
303: public static WrappedConnection getWrappedConnection(
304: Connection connection) {
305: return (WrappedConnection) ((Factory) connection)
306: .getCallback(0);
307: }
308:
309: }
310:
311: /*
312: Revision history:
313: $Log: ProxyFactory.java,v $
314: Revision 1.33 2006/04/09 21:08:43 billhorsman
315: Use our own naming policy for Cglib to avoid duplicate class definition exceptions.
316:
317: Revision 1.32 2006/01/18 14:40:02 billhorsman
318: Unbundled Jakarta's Commons Logging.
319:
320: Revision 1.31 2005/09/26 09:59:22 billhorsman
321: Explicitly use the ProxyFactory class loader when using Cglib's Enhancer to avoid class loading issues.
322:
323: Revision 1.30 2005/05/04 16:31:41 billhorsman
324: Use the definition referenced by the proxy connection rather than the pool instead.
325:
326: Revision 1.29 2004/07/27 21:44:15 billhorsman
327: Remove insane amount of debug logging.
328:
329: Revision 1.28 2004/06/17 21:58:36 billhorsman
330: Injectable interface fixes.
331:
332: Revision 1.27 2004/06/02 20:50:47 billhorsman
333: Dropped obsolete InvocationHandler reference and injectable interface stuff.
334:
335: Revision 1.26 2004/03/23 21:19:45 billhorsman
336: Added disposable wrapper to proxied connection. And made proxied objects implement delegate interfaces too.
337:
338: Revision 1.25 2003/12/12 19:29:47 billhorsman
339: Now uses Cglib 2.0
340:
341: Revision 1.24 2003/09/30 18:39:08 billhorsman
342: New test-before-use, test-after-use and fatal-sql-exception-wrapper-class properties.
343:
344: Revision 1.23 2003/09/10 22:21:04 chr32
345: Removing > jdk 1.2 dependencies.
346:
347: Revision 1.22 2003/09/07 22:11:31 billhorsman
348: Remove very persistent debug message
349:
350: Revision 1.21 2003/08/27 18:03:20 billhorsman
351: added new getDelegateConnection() method
352:
353: Revision 1.20 2003/03/11 14:51:54 billhorsman
354: more concurrency fixes relating to snapshots
355:
356: Revision 1.19 2003/03/10 23:43:13 billhorsman
357: reapplied checkstyle that i'd inadvertently let
358: IntelliJ change...
359:
360: Revision 1.18 2003/03/10 15:26:49 billhorsman
361: refactoringn of concurrency stuff (and some import
362: optimisation)
363:
364: Revision 1.17 2003/03/05 18:42:33 billhorsman
365: big refactor of prototyping and house keeping to
366: drastically reduce the number of threads when using
367: many pools
368:
369: Revision 1.16 2003/03/03 11:11:58 billhorsman
370: fixed licence
371:
372: Revision 1.15 2003/02/19 15:14:32 billhorsman
373: fixed copyright (copy and paste error,
374: not copyright change)
375:
376: Revision 1.14 2003/02/12 12:28:27 billhorsman
377: added url, proxyHashcode and delegateHashcode to
378: ConnectionInfoIF
379:
380: Revision 1.13 2003/02/06 17:41:04 billhorsman
381: now uses imported logging
382:
383: Revision 1.12 2003/01/31 14:33:18 billhorsman
384: fix for DatabaseMetaData
385:
386: Revision 1.11 2003/01/27 18:26:39 billhorsman
387: refactoring of ProxyConnection and ProxyStatement to
388: make it easier to write JDK 1.2 patch
389:
390: Revision 1.10 2002/12/16 11:15:19 billhorsman
391: fixed getDelegateStatement
392:
393: Revision 1.9 2002/12/16 10:57:47 billhorsman
394: add getDelegateStatement to allow access to the
395: delegate JDBC driver's Statement
396:
397: Revision 1.8 2002/12/12 10:48:25 billhorsman
398: checkstyle
399:
400: Revision 1.7 2002/12/08 22:17:35 billhorsman
401: debug for proxying statement interfaces
402:
403: Revision 1.6 2002/12/06 15:57:08 billhorsman
404: fix for proxied statement where Statement interface is not directly
405: implemented.
406:
407: Revision 1.5 2002/12/03 12:24:00 billhorsman
408: fixed fatal sql exception
409:
410: Revision 1.4 2002/11/09 15:56:52 billhorsman
411: fix doc
412:
413: Revision 1.3 2002/11/02 14:22:15 billhorsman
414: Documentation
415:
416: Revision 1.2 2002/10/30 21:25:08 billhorsman
417: move createStatement into ProxyFactory
418:
419: Revision 1.1 2002/10/30 21:19:16 billhorsman
420: make use of ProxyFactory
421:
422: */
|