KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > logicalcobwebs > proxool > WrappedConnection


1 /*
2  * This software is released under a licence similar to the Apache Software Licence.
3  * See org.logicalcobwebs.proxool.package.html for details.
4  * The latest version is available at http://proxool.sourceforge.net
5  */

6 package org.logicalcobwebs.proxool;
7
8 import org.logicalcobwebs.cglib.proxy.InvocationHandler;
9 import org.logicalcobwebs.cglib.proxy.MethodInterceptor;
10 import org.logicalcobwebs.cglib.proxy.MethodProxy;
11 import org.apache.commons.logging.Log;
12 import org.apache.commons.logging.LogFactory;
13 import org.logicalcobwebs.proxool.proxy.InvokerFacade;
14
15 import java.lang.reflect.Method JavaDoc;
16 import java.lang.reflect.InvocationTargetException JavaDoc;
17 import java.sql.Statement JavaDoc;
18 import java.sql.SQLException JavaDoc;
19 import java.sql.Connection JavaDoc;
20
21 /**
22  * Wraps up a {@link ProxyConnection}. It is proxied as a {@link java.sql.Connection}
23  * @version $Revision: 1.6 $, $Date: 2006/01/18 14:40:02 $
24  * @author <a HREF="mailto:bill@logicalcobwebs.co.uk">Bill Horsman</a>
25  * @author $Author: billhorsman $ (current maintainer)
26  * @since Proxool 0.9
27  */

28 public class WrappedConnection implements MethodInterceptor {
29
30     private static final Log LOG = LogFactory.getLog(WrappedConnection.class);
31
32     private static final String JavaDoc CLOSE_METHOD = "close";
33
34     private static final String JavaDoc IS_CLOSED_METHOD = "isClosed";
35
36     private static final String JavaDoc EQUALS_METHOD = "equals";
37
38     private static final String JavaDoc GET_META_DATA_METHOD = "getMetaData";
39
40     private static final String JavaDoc FINALIZE_METHOD = "finalize";
41
42     private static final String JavaDoc HASH_CODE_METHOD = "hashCode";
43
44     private static final String JavaDoc TO_STRING_METHOD = "toString";
45
46     /**
47      * The wrapped object. We should protect this and not expose it. We have to make sure that
48      * if we pass the proxyConnection to another WrappedConnection then this one can no longer
49      * manipulate it.
50      */

51     private ProxyConnection proxyConnection;
52
53     private long id;
54
55     private String JavaDoc alias;
56
57     /**
58      * This gets set if the close() method is explicitly called. The {@link #getProxyConnection() proxyConnection}
59      * could still be {@link org.logicalcobwebs.proxool.ProxyConnectionIF#isReallyClosed() really closed} without
60      * this wrapper knowing about it yet.
61      */

62     private boolean manuallyClosed;
63
64     /**
65      * Construct this wrapper around the proxy connection
66      * @param proxyConnection to wrap
67      */

68     public WrappedConnection(ProxyConnection proxyConnection) {
69         this.proxyConnection = proxyConnection;
70         this.id = proxyConnection.getId();
71         this.alias= proxyConnection.getDefinition().getAlias();
72     }
73
74     /**
75      * Get the encapsulated proxy connection
76      * @return the proxy connection
77      */

78     public ProxyConnection getProxyConnection() {
79         return proxyConnection;
80     }
81
82     /**
83      * Delegates to {@link #invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) invoke}
84      * @see MethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], org.logicalcobwebs.cglib.proxy.MethodProxy)
85      */

86     public Object JavaDoc intercept(Object JavaDoc obj, Method JavaDoc method, Object JavaDoc[] args, MethodProxy proxy) throws Throwable JavaDoc {
87         return invoke(proxy, method, args);
88     }
89
90     /**
91      * Delegates all operations to the encapsulated {@link ProxyConnection} except for:
92      * <ul>
93      * <li>close()</li>
94      * <li>equals()</li>
95      * <li>hashCode()</li>
96      * <li>isClosed()</li>
97      * <li>getMetaData()</li>
98      * <li>finalize()</li>
99      * </ul>
100      * It also spots mutators and remembers that the property has been changed so that it can
101      * be {@link ConnectionResetter reset}. And any statements that are returned are remembered
102      * so that we can track whether all statements have been closed properly when the connection
103      * is returned to the pool.
104      * @see InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
105      */

106     public Object JavaDoc invoke(Object JavaDoc proxy, Method JavaDoc method, Object JavaDoc[] args) throws Throwable JavaDoc {
107         Object JavaDoc result = null;
108         int argCount = args != null ? args.length : 0;
109         Method JavaDoc concreteMethod = method;
110         if (proxyConnection != null && proxyConnection.getConnection() != null) {
111             concreteMethod = InvokerFacade.getConcreteMethod(proxyConnection.getConnection().getClass(), method);
112         }
113         try {
114             if (proxyConnection != null && proxyConnection.isReallyClosed()) {
115                 // The user is trying to do something to this connection and it's been closed.
116
if (concreteMethod.getName().equals(IS_CLOSED_METHOD)) {
117                     // That's cool. No problem checking as many times as you like.
118
} else if (concreteMethod.getName().equals(CLOSE_METHOD)) {
119                     // That's cool. You can call close as often as you like.
120
} else if (manuallyClosed) {
121                     // We've already manually closed this connection yet we trying to do something
122
// to it that isn't another close(). That is bad client coding :)
123
throw new SQLException JavaDoc("You can't perform any operations on a connection after you've called close()");
124                 } else {
125                     // The connection has been closed automatically. The client probably wasn't expecting
126
// that. Still, throw an exception so that they know it's all gone pear shaped.
127
throw new SQLException JavaDoc("You can't perform any operations on this connection. It has been automatically closed by Proxool for some reason (see logs).");
128                 }
129             }
130             if (concreteMethod.getName().equals(CLOSE_METHOD)) {
131                 // It's okay to close a connection twice. Only we ignore the
132
// second time.
133
if (proxyConnection != null && !proxyConnection.isReallyClosed()) {
134                     proxyConnection.close();
135                     // Set it to null so that we can't do anything else to it.
136
proxyConnection = null;
137                     manuallyClosed = true;
138                 }
139             } else if (concreteMethod.getName().equals(EQUALS_METHOD) && argCount == 1) {
140                 result = equals(args[0]) ? Boolean.TRUE : Boolean.FALSE;
141             } else if (concreteMethod.getName().equals(HASH_CODE_METHOD) && argCount == 0) {
142                 result = new Integer JavaDoc(hashCode());
143             } else if (concreteMethod.getName().equals(IS_CLOSED_METHOD) && argCount == 0) {
144                 result = (proxyConnection == null || proxyConnection.isClosed()) ? Boolean.TRUE : Boolean.FALSE;
145             } else if (concreteMethod.getName().equals(GET_META_DATA_METHOD) && argCount == 0) {
146                 if (proxyConnection != null) {
147                     Connection JavaDoc connection = ProxyFactory.getWrappedConnection(proxyConnection);
148                     result = ProxyFactory.getDatabaseMetaData(proxyConnection.getConnection().getMetaData(), connection);
149                 } else {
150                     throw new SQLException JavaDoc("You can't perform a " + concreteMethod.getName() + " operation after the connection has been closed");
151                 }
152             } else if (concreteMethod.getName().equals(FINALIZE_METHOD)) {
153                 super.finalize();
154             } else if (concreteMethod.getName().equals(TO_STRING_METHOD)) {
155                 result = toString();
156             } else {
157                 if (proxyConnection != null) {
158                     if (concreteMethod.getName().startsWith(ConnectionResetter.MUTATOR_PREFIX)) {
159                         proxyConnection.setNeedToReset(true);
160                     }
161                     try {
162                         result = concreteMethod.invoke(proxyConnection.getConnection(), args);
163                     } catch (IllegalAccessException JavaDoc e) {
164                         // This is probably because we are trying to access a non-public concrete class. But don't worry,
165
// we can always use the proxy supplied method. This will only fail if we try to use an injectable
166
// method on a method in a class that isn't public and for a method that isn't declared in an interface -
167
// but if that is the case then that method is inaccessible by any means (even by bypassing Proxool and
168
// using the vendor's driver directly).
169
LOG.debug("Ignoring IllegalAccessException whilst invoking the " + concreteMethod + " concrete method and trying the " + method + " method directly.");
170                         // By overriding the method cached in the InvokerFacade we ensure that we only log this message once, and
171
// we speed up subsequent usages by not calling the method that fails first.
172
InvokerFacade.overrideConcreteMethod(proxyConnection.getConnection().getClass(), method, method);
173                         result = method.invoke(proxyConnection.getConnection(), args);
174                     }
175                 } else {
176                     throw new SQLException JavaDoc("You can't perform a " + concreteMethod.getName() + " operation after the connection has been closed");
177                 }
178             }
179
180             // If we have just made some sort of Statement then we should rather return
181
// a proxy instead.
182
if (result instanceof Statement JavaDoc) {
183                 // Work out whether we were passed the sql statement during the
184
// call to get the statement object. Sometimes you do, sometimes
185
// you don't:
186
// connection.prepareCall(sql);
187
// connection.createProxyStatement();
188
String JavaDoc sqlStatement = null;
189                 if (argCount > 0 && args[0] instanceof String JavaDoc) {
190                     sqlStatement = (String JavaDoc) args[0];
191                 }
192
193                 // We keep a track of all open statements
194
proxyConnection.addOpenStatement((Statement JavaDoc) result);
195
196                 result = ProxyFactory.getStatement((Statement JavaDoc) result, proxyConnection.getConnectionPool(), proxyConnection, sqlStatement);
197
198             }
199
200         } catch (InvocationTargetException JavaDoc e) {
201             // We might get a fatal exception here. Let's test for it.
202
if (FatalSqlExceptionHelper.testException(proxyConnection.getDefinition(), e.getTargetException())) {
203                 FatalSqlExceptionHelper.throwFatalSQLException(proxyConnection.getDefinition().getFatalSqlExceptionWrapper(), e.getTargetException());
204             }
205             throw e.getTargetException();
206         } catch (SQLException JavaDoc e) {
207             throw new SQLException JavaDoc("Couldn't perform the operation " + concreteMethod.getName() + ": " + e.getMessage());
208         } catch (Exception JavaDoc e) {
209             LOG.error("Unexpected invocation exception", e);
210             if (FatalSqlExceptionHelper.testException(proxyConnection.getDefinition(), e)) {
211                 FatalSqlExceptionHelper.throwFatalSQLException(proxyConnection.getDefinition().getFatalSqlExceptionWrapper(), e);
212             }
213             throw new RuntimeException JavaDoc("Unexpected invocation exception: "
214                     + e.getMessage());
215         }
216         return result;
217     }
218
219     /**
220      * The ID for the encapsulated {@link ProxyConnection}. This will still
221      * return the correct value after the connection is closed.
222      * @return the ID
223      */

224     public long getId() {
225         return id;
226     }
227
228     /**
229      * Get the alias of the connection pool this connection belongs to
230      * @return {@link ConnectionPoolDefinitionIF#getAlias() alias}
231      */

232     public String JavaDoc getAlias() {
233         return alias;
234     }
235
236     /**
237      * If the object passed to this method is actually a proxied version of this
238      * class then compare the real class with this one.
239      * @param obj the object to compare
240      * @return true if the object is a proxy of "this"
241      */

242     public boolean equals(Object JavaDoc obj) {
243         if (obj instanceof Connection JavaDoc) {
244             final WrappedConnection wc = ProxyFactory.getWrappedConnection((Connection JavaDoc) obj);
245             if (wc != null && wc.getId() > 0 && getId() > 0) {
246                 return wc.getId() == getId();
247             } else {
248                 return false;
249             }
250         } else {
251             return false;
252         }
253     }
254
255     /**
256      * @see Object#toString()
257      */

258     public String JavaDoc toString() {
259         if (proxyConnection != null) {
260             return hashCode() + "(" + proxyConnection.getConnection().toString() + ")";
261         } else {
262             return hashCode() + "(out of scope)";
263         }
264     }
265 }
266 /*
267  Revision history:
268  $Log: WrappedConnection.java,v $
269  Revision 1.6 2006/01/18 14:40:02 billhorsman
270  Unbundled Jakarta's Commons Logging.
271
272  Revision 1.5 2005/10/02 12:32:58 billhorsman
273  Improve the trapping of operations after a wrapped connection is closed.
274
275  Revision 1.4 2005/05/04 16:31:41 billhorsman
276  Use the definition referenced by the proxy connection rather than the pool instead.
277
278  Revision 1.3 2004/07/13 21:06:21 billhorsman
279  Fix problem using injectable interfaces on methods that are declared in non-public classes.
280
281  Revision 1.2 2004/06/02 20:50:47 billhorsman
282  Dropped obsolete InvocationHandler reference and injectable interface stuff.
283
284  Revision 1.1 2004/03/23 21:19:45 billhorsman
285  Added disposable wrapper to proxied connection. And made proxied objects implement delegate interfaces too.
286
287 */
Popular Tags