001: /*
002: * HA-JDBC: High-Availability JDBC
003: * Copyright (c) 2004-2007 Paul Ferraro
004: *
005: * This library is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU Lesser General Public License as published by the
007: * Free Software Foundation; either version 2.1 of the License, or (at your
008: * option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful, but WITHOUT
011: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
012: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
013: * for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public License
016: * along with this library; if not, write to the Free Software Foundation,
017: * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: *
019: * Contact: ferraro@users.sourceforge.net
020: */
021: package net.sf.hajdbc.sql;
022:
023: import java.lang.reflect.Method;
024: import java.lang.reflect.Proxy;
025: import java.sql.Connection;
026: import java.sql.SQLException;
027: import java.sql.Savepoint;
028: import java.util.Map;
029: import java.util.Set;
030:
031: import net.sf.hajdbc.Database;
032: import net.sf.hajdbc.util.reflect.Methods;
033:
034: /**
035: * @author Paul Ferraro
036: * @param <D>
037: * @param <P>
038: */
039: @SuppressWarnings("nls")
040: public class ConnectionInvocationHandler<D, P> extends
041: AbstractChildInvocationHandler<D, P, Connection> {
042: private static final Set<Method> driverReadMethodSet = Methods
043: .findMethods(Connection.class,
044: "create(ArrayOf|Blob|Clob|NClob|SQLXML|Struct)",
045: "getAutoCommit", "getCatalog", "getClientInfo",
046: "getHoldability", "getTypeMap", "getWarnings",
047: "isClosed", "isReadOnly", "nativeSQL");
048: private static final Set<Method> databaseReadMethodSet = Methods
049: .findMethods(Connection.class, "getMetaData",
050: "getTransactionIsolation", "isValid");
051: private static final Set<Method> driverWriterMethodSet = Methods
052: .findMethods(Connection.class, "clearWarnings",
053: "setAutoCommit", "setClientInfo", "setHoldability",
054: "setTypeMap");
055: private static final Set<Method> endTransactionMethodSet = Methods
056: .findMethods(Connection.class, "commit", "rollback");
057: private static final Set<Method> createStatementMethodSet = Methods
058: .findMethods(Connection.class, "createStatement");
059: private static final Set<Method> prepareStatementMethodSet = Methods
060: .findMethods(Connection.class, "prepareStatement");
061: private static final Set<Method> prepareCallMethodSet = Methods
062: .findMethods(Connection.class, "prepareCall");
063: private static final Set<Method> setSavepointMethodSet = Methods
064: .findMethods(Connection.class, "setSavepoint");
065:
066: private static final Method releaseSavepointMethod = Methods
067: .getMethod(Connection.class, "releaseSavepoint",
068: Savepoint.class);
069: private static final Method rollbackSavepointMethod = Methods
070: .getMethod(Connection.class, "rollback", Savepoint.class);
071: private static final Method closeMethod = Methods.getMethod(
072: Connection.class, "close");
073:
074: private FileSupport fileSupport;
075: private TransactionContext<D> transactionContext;
076:
077: /**
078: * @param proxy
079: * @param handler
080: * @param invoker
081: * @param connectionMap
082: * @param transactionContext
083: * @param fileSupport
084: * @throws Exception
085: */
086: public ConnectionInvocationHandler(P proxy, SQLProxy<D, P> handler,
087: Invoker<D, P, Connection> invoker,
088: Map<Database<D>, Connection> connectionMap,
089: TransactionContext<D> transactionContext,
090: FileSupport fileSupport) throws Exception {
091: super (proxy, handler, invoker, Connection.class, connectionMap);
092:
093: this .transactionContext = transactionContext;
094: this .fileSupport = fileSupport;
095: }
096:
097: /**
098: * @see net.sf.hajdbc.sql.AbstractChildInvocationHandler#getInvocationStrategy(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
099: */
100: @Override
101: protected InvocationStrategy<D, Connection, ?> getInvocationStrategy(
102: Connection connection, Method method, Object[] parameters)
103: throws Exception {
104: if (driverReadMethodSet.contains(method)) {
105: return new DriverReadInvocationStrategy<D, Connection, Object>();
106: }
107:
108: if (databaseReadMethodSet.contains(method)) {
109: return new DatabaseReadInvocationStrategy<D, Connection, Object>();
110: }
111:
112: if (driverWriterMethodSet.contains(method)) {
113: return new DriverWriteInvocationStrategy<D, Connection, Object>();
114: }
115:
116: if (endTransactionMethodSet.contains(method)) {
117: return this .transactionContext
118: .end(new DatabaseWriteInvocationStrategy<D, Connection, Void>(
119: this .cluster.getTransactionalExecutor()));
120: }
121:
122: if (method.equals(rollbackSavepointMethod)
123: || method.equals(releaseSavepointMethod)) {
124: return new DatabaseWriteInvocationStrategy<D, Connection, Void>(
125: this .cluster.getTransactionalExecutor());
126: }
127:
128: boolean createStatement = createStatementMethodSet
129: .contains(method);
130: boolean prepareStatement = prepareStatementMethodSet
131: .contains(method);
132: boolean prepareCall = prepareCallMethodSet.contains(method);
133:
134: if (createStatement || prepareStatement || prepareCall) {
135: if (connection.isReadOnly()) {
136: return createStatement ? new DriverReadInvocationStrategy<D, Connection, Object>()
137: : new DatabaseReadInvocationStrategy<D, Connection, Object>();
138: }
139:
140: if (createStatement) {
141: return new StatementInvocationStrategy<D>(connection,
142: this .transactionContext, this .fileSupport);
143: }
144:
145: if (prepareStatement) {
146: return new PreparedStatementInvocationStrategy<D>(
147: this .cluster, connection,
148: this .transactionContext, this .fileSupport,
149: (String) parameters[0]);
150: }
151:
152: if (prepareCall) {
153: return new CallableStatementInvocationStrategy<D>(
154: this .cluster, connection,
155: this .transactionContext, this .fileSupport);
156: }
157: }
158:
159: if (setSavepointMethodSet.contains(method)) {
160: return new SavepointInvocationStrategy<D>(this .cluster,
161: connection);
162: }
163: /*
164: if (methodName.equals("createBlob"))
165: {
166: return new BlobInvocationStrategy<D, Connection>(connection);
167: }
168:
169: if (methodName.equals("createClob") || methodName.equals("createNClob"))
170: {
171: return new ClobInvocationStrategy<D, Connection>(connection, method.getReturnType().asSubclass(Clob.class));
172: }
173: */
174: return super .getInvocationStrategy(connection, method,
175: parameters);
176: }
177:
178: /**
179: * @see net.sf.hajdbc.sql.AbstractChildInvocationHandler#getInvoker(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
180: */
181: @SuppressWarnings("unchecked")
182: @Override
183: protected Invoker<D, Connection, ?> getInvoker(
184: Connection connection, Method method, Object[] parameters)
185: throws Exception {
186: if (method.equals(releaseSavepointMethod)) {
187: final SQLProxy<D, Savepoint> proxy = (SQLProxy) Proxy
188: .getInvocationHandler(parameters[0]);
189:
190: return new Invoker<D, Connection, Void>() {
191: public Void invoke(Database<D> database,
192: Connection connection) throws SQLException {
193: connection.releaseSavepoint(proxy
194: .getObject(database));
195:
196: return null;
197: }
198: };
199: }
200:
201: if (method.equals(rollbackSavepointMethod)) {
202: final SQLProxy<D, Savepoint> proxy = (SQLProxy) Proxy
203: .getInvocationHandler(parameters[0]);
204:
205: return new Invoker<D, Connection, Void>() {
206: public Void invoke(Database<D> database,
207: Connection connection) throws SQLException {
208: connection.rollback(proxy.getObject(database));
209:
210: return null;
211: }
212: };
213: }
214:
215: return super .getInvoker(connection, method, parameters);
216: }
217:
218: /**
219: * @see net.sf.hajdbc.sql.AbstractChildInvocationHandler#isSQLMethod(java.lang.reflect.Method)
220: */
221: @Override
222: protected boolean isSQLMethod(Method method) {
223: return prepareStatementMethodSet.contains(method);
224: }
225:
226: /**
227: * @see net.sf.hajdbc.sql.AbstractChildInvocationHandler#postInvoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
228: */
229: @SuppressWarnings("unchecked")
230: @Override
231: protected void postInvoke(Connection object, Method method,
232: Object[] parameters) {
233: if (method.equals(closeMethod)) {
234: this .transactionContext.close();
235:
236: this .fileSupport.close();
237:
238: this .getParentProxy().removeChild(this );
239: } else if (method.equals(releaseSavepointMethod)) {
240: SQLProxy<D, Savepoint> proxy = (SQLProxy) Proxy
241: .getInvocationHandler(parameters[0]);
242:
243: this .removeChild(proxy);
244: }
245: }
246:
247: /**
248: * @see net.sf.hajdbc.sql.AbstractChildInvocationHandler#close(java.lang.Object, java.lang.Object)
249: */
250: @Override
251: protected void close(P parent, Connection connection)
252: throws SQLException {
253: connection.close();
254: }
255: }
|