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.io.File;
024: import java.io.InputStream;
025: import java.io.Reader;
026: import java.lang.reflect.InvocationHandler;
027: import java.lang.reflect.Method;
028: import java.lang.reflect.Proxy;
029: import java.sql.Blob;
030: import java.sql.Clob;
031: import java.sql.Connection;
032: import java.sql.PreparedStatement;
033: import java.sql.ResultSet;
034: import java.sql.SQLException;
035: import java.util.ArrayList;
036: import java.util.Arrays;
037: import java.util.Collections;
038: import java.util.List;
039: import java.util.Map;
040: import java.util.Set;
041: import java.util.concurrent.locks.Lock;
042:
043: import javax.sql.rowset.serial.SerialBlob;
044: import javax.sql.rowset.serial.SerialClob;
045:
046: import net.sf.hajdbc.Database;
047: import net.sf.hajdbc.util.reflect.Methods;
048: import net.sf.hajdbc.util.reflect.ProxyFactory;
049: import net.sf.hajdbc.util.reflect.SimpleInvocationHandler;
050:
051: /**
052: * @author Paul Ferraro
053: * @param <D>
054: * @param <S>
055: */
056: @SuppressWarnings("nls")
057: public class AbstractPreparedStatementInvocationHandler<D, S extends PreparedStatement>
058: extends AbstractStatementInvocationHandler<D, S> {
059: private static final Set<Method> databaseReadMethodSet = Methods
060: .findMethods(PreparedStatement.class, "getMetaData",
061: "getParameterMetaData");
062: private static final Set<Method> driverWriteMethodSet = Methods
063: .findMethods(PreparedStatement.class, "addBatch",
064: "clearParameters", "set\\w+");
065: private static final Method executeMethod = Methods.getMethod(
066: PreparedStatement.class, "execute");
067: private static final Method executeUpdateMethod = Methods
068: .getMethod(PreparedStatement.class, "executeUpdate");
069: private static final Method executeQueryMethod = Methods.getMethod(
070: PreparedStatement.class, "executeQuery");
071:
072: protected List<Lock> lockList = Collections.emptyList();
073: protected boolean selectForUpdate = false;
074:
075: /**
076: * @param connection
077: * @param proxy
078: * @param invoker
079: * @param statementClass
080: * @param statementMap
081: * @param transactionContext
082: * @param fileSupport
083: * @throws Exception
084: */
085: public AbstractPreparedStatementInvocationHandler(
086: Connection connection, SQLProxy<D, Connection> proxy,
087: Invoker<D, Connection, S> invoker, Class<S> statementClass,
088: Map<Database<D>, S> statementMap,
089: TransactionContext<D> transactionContext,
090: FileSupport fileSupport) throws Exception {
091: super (connection, proxy, invoker, statementClass, statementMap,
092: transactionContext, fileSupport);
093: }
094:
095: /**
096: * @see net.sf.hajdbc.sql.AbstractStatementInvocationHandler#getInvocationStrategy(java.sql.Statement, java.lang.reflect.Method, java.lang.Object[])
097: */
098: @Override
099: protected InvocationStrategy<D, S, ?> getInvocationStrategy(
100: S statement, Method method, Object[] parameters)
101: throws Exception {
102: if (databaseReadMethodSet.contains(method)) {
103: return new DatabaseReadInvocationStrategy<D, S, Object>();
104: }
105:
106: if (driverWriteMethodSet.contains(method)) {
107: return new DriverWriteInvocationStrategy<D, S, Object>();
108: }
109:
110: if (method.equals(executeMethod)
111: || method.equals(executeUpdateMethod)) {
112: return this .transactionContext
113: .start(
114: new LockingInvocationStrategy<D, S, Object>(
115: new DatabaseWriteInvocationStrategy<D, S, Object>(
116: this .cluster
117: .getTransactionalExecutor()),
118: this .lockList), this .getParent());
119: }
120:
121: if (method.equals(executeQueryMethod)) {
122: int concurrency = statement.getResultSetConcurrency();
123:
124: if (this .lockList.isEmpty()
125: && (concurrency == ResultSet.CONCUR_READ_ONLY)
126: && !this .selectForUpdate) {
127: return new DatabaseReadInvocationStrategy<D, S, Object>();
128: }
129:
130: InvocationStrategy<D, S, ResultSet> strategy = new LockingInvocationStrategy<D, S, ResultSet>(
131: new EagerResultSetInvocationStrategy<D, S>(
132: this .cluster, statement,
133: this .transactionContext, this .fileSupport),
134: this .lockList);
135:
136: return this .selectForUpdate ? this .transactionContext
137: .start(strategy, this .getParent()) : strategy;
138: }
139:
140: return super .getInvocationStrategy(statement, method,
141: parameters);
142: }
143:
144: /**
145: * @see net.sf.hajdbc.sql.AbstractChildInvocationHandler#getInvoker(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
146: */
147: @SuppressWarnings("unchecked")
148: @Override
149: protected Invoker<D, S, ?> getInvoker(S statement,
150: final Method method, final Object[] parameters)
151: throws Exception {
152: Class<?>[] types = method.getParameterTypes();
153:
154: if (this .isIndexSetMethod(method)) {
155: Class<?> type = types[1];
156:
157: if (type.equals(InputStream.class)) {
158: final File file = this .fileSupport
159: .createFile((InputStream) parameters[1]);
160:
161: return new Invoker<D, S, Object>() {
162: public Object invoke(Database<D> database,
163: S statement) throws SQLException {
164: List<Object> parameterList = new ArrayList<Object>(
165: Arrays.asList(parameters));
166:
167: parameterList
168: .set(
169: 1,
170: AbstractPreparedStatementInvocationHandler.this .fileSupport
171: .getInputStream(file));
172:
173: return Methods.invoke(method, statement,
174: parameterList.toArray());
175: }
176: };
177: }
178:
179: if (type.equals(Reader.class)) {
180: final File file = this .fileSupport
181: .createFile((Reader) parameters[1]);
182:
183: return new Invoker<D, S, Object>() {
184: public Object invoke(Database<D> database,
185: S statement) throws SQLException {
186: List<Object> parameterList = new ArrayList<Object>(
187: Arrays.asList(parameters));
188:
189: parameterList
190: .set(
191: 1,
192: AbstractPreparedStatementInvocationHandler.this .fileSupport
193: .getReader(file));
194:
195: return Methods.invoke(method, statement,
196: parameterList.toArray());
197: }
198: };
199: }
200:
201: if (type.equals(Blob.class)) {
202: Blob blob = (Blob) parameters[1];
203:
204: if (Proxy.isProxyClass(blob.getClass())) {
205: InvocationHandler handler = Proxy
206: .getInvocationHandler(blob);
207:
208: if (BlobInvocationHandler.class.isInstance(handler)) {
209: final BlobInvocationHandler<D, ?> proxy = (BlobInvocationHandler) handler;
210:
211: return new Invoker<D, S, Object>() {
212: public Object invoke(Database<D> database,
213: S statement) throws SQLException {
214: List<Object> parameterList = new ArrayList<Object>(
215: Arrays.asList(parameters));
216:
217: parameterList.set(1, proxy
218: .getObject(database));
219:
220: return Methods.invoke(method,
221: statement, parameterList
222: .toArray());
223: }
224: };
225: }
226: }
227:
228: parameters[1] = new SerialBlob(blob);
229: }
230:
231: // Handle both clob and nclob
232: if (Clob.class.isAssignableFrom(type)) {
233: Clob clob = (Clob) parameters[1];
234:
235: if (Proxy.isProxyClass(clob.getClass())) {
236: InvocationHandler handler = Proxy
237: .getInvocationHandler(clob);
238:
239: if (ClobInvocationHandler.class.isInstance(handler)) {
240: final ClobInvocationHandler<D, ?> proxy = (ClobInvocationHandler) handler;
241:
242: return new Invoker<D, S, Object>() {
243: public Object invoke(Database<D> database,
244: S statement) throws SQLException {
245: List<Object> parameterList = new ArrayList<Object>(
246: Arrays.asList(parameters));
247:
248: parameterList.set(1, proxy
249: .getObject(database));
250:
251: return Methods.invoke(method,
252: statement, parameterList
253: .toArray());
254: }
255: };
256: }
257: }
258:
259: Clob serialClob = new SerialClob(clob);
260:
261: parameters[1] = type.equals(Clob.class) ? serialClob
262: : ProxyFactory
263: .createProxy(type,
264: new SimpleInvocationHandler(
265: serialClob));
266: }
267: }
268:
269: return super .getInvoker(statement, method, parameters);
270: }
271:
272: private boolean isIndexSetMethod(Method method) {
273: Class<?>[] types = method.getParameterTypes();
274:
275: return this .isSetMethod(method) && (types.length > 1)
276: && this .isIndexType(types[0]);
277: }
278:
279: protected boolean isIndexType(Class<?> type) {
280: return type.equals(Integer.TYPE);
281: }
282:
283: /**
284: * @see net.sf.hajdbc.sql.AbstractStatementInvocationHandler#isRecordable(java.lang.reflect.Method)
285: */
286: @Override
287: protected boolean isRecordable(Method method) {
288: return super .isRecordable(method)
289: || method.getName().equals("clearParameters")
290: || this.isIndexSetMethod(method);
291: }
292: }
|