001/* 002 * HA-JDBC: High-Availability JDBC 003 * Copyright (C) 2012 Paul Ferraro 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Lesser General Public License as published by 007 * the Free Software Foundation, either version 3 of the License, or 008 * (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018package net.sf.hajdbc.sql; 019 020import java.lang.reflect.Method; 021import java.sql.Clob; 022import java.sql.NClob; 023import java.sql.ResultSet; 024import java.sql.SQLException; 025import java.sql.Statement; 026import java.util.Set; 027 028import net.sf.hajdbc.Database; 029import net.sf.hajdbc.invocation.InvocationStrategies; 030import net.sf.hajdbc.invocation.InvocationStrategy; 031import net.sf.hajdbc.invocation.Invoker; 032import net.sf.hajdbc.util.reflect.Methods; 033 034/** 035 * @author Paul Ferraro 036 * @param <D> 037 * @param <S> 038 */ 039@SuppressWarnings("nls") 040public class ResultSetInvocationHandler<Z, D extends Database<Z>, S extends Statement> extends InputSinkRegistryInvocationHandler<Z, D, S, ResultSet, ResultSetProxyFactory<Z, D, S>> 041{ 042 private static final Set<Method> driverReadMethodSet = Methods.findMethods(ResultSet.class, "findColumn", "getConcurrency", "getCursorName", "getFetchDirection", "getFetchSize", "getHoldability", "getMetaData", "getRow", "getType", "getWarnings", "isAfterLast", "isBeforeFirst", "isClosed", "isFirst", "isLast", "row(Deleted|Inserted|Updated)", "wasNull"); 043 private static final Set<Method> driverWriteMethodSet = Methods.findMethods(ResultSet.class, "absolute", "afterLast", "beforeFirst", "cancelRowUpdates", "clearWarnings", "first", "last", "moveTo(Current|Insert)Row", "next", "previous", "relative", "setFetchDirection", "setFetchSize"); 044 private static final Set<Method> transactionalWriteMethodSet = Methods.findMethods(ResultSet.class, "(delete|insert|update)Row"); 045 private static final Set<Method> getArrayMethodSet = Methods.findMethods(ResultSet.class, "getArray"); 046 private static final Set<Method> getBlobMethodSet = Methods.findMethods(ResultSet.class, "getBlob"); 047 private static final Set<Method> getClobMethodSet = Methods.findMethods(ResultSet.class, "getClob"); 048 private static final Set<Method> getNClobMethodSet = Methods.findMethods(ResultSet.class, "getNClob"); 049 private static final Set<Method> getRefMethodSet = Methods.findMethods(ResultSet.class, "getRef"); 050 private static final Set<Method> getSQLXMLMethodSet = Methods.findMethods(ResultSet.class, "getSQLXML"); 051 052 private static final Method closeMethod = Methods.getMethod(ResultSet.class, "close"); 053 private static final Method getStatementMethod = Methods.getMethod(ResultSet.class, "getStatement"); 054 055 /** 056 * @param factory a factory for creating result set proxies 057 */ 058 public ResultSetInvocationHandler(ResultSetProxyFactory<Z, D, S> factory) 059 { 060 super(ResultSet.class, factory, getStatementMethod); 061 } 062 063 @Override 064 protected ProxyFactoryFactory<Z, D, ResultSet, SQLException, ?, ? extends Exception> getProxyFactoryFactory(ResultSet object, Method method, Object... parameters) throws SQLException 065 { 066 if (getArrayMethodSet.contains(method)) 067 { 068 return new ArrayProxyFactoryFactory<Z, D, ResultSet>(this.getProxyFactory().locatorsUpdateCopy()); 069 } 070 if (getBlobMethodSet.contains(method)) 071 { 072 return new BlobProxyFactoryFactory<Z, D, ResultSet>(this.getProxyFactory().locatorsUpdateCopy()); 073 } 074 if (getClobMethodSet.contains(method)) 075 { 076 return new ClobProxyFactoryFactory<Z, D, ResultSet, Clob>(Clob.class, this.getProxyFactory().locatorsUpdateCopy()); 077 } 078 if (getNClobMethodSet.contains(method)) 079 { 080 return new ClobProxyFactoryFactory<Z, D, ResultSet, NClob>(NClob.class, this.getProxyFactory().locatorsUpdateCopy()); 081 } 082 if (getRefMethodSet.contains(method)) 083 { 084 return new RefProxyFactoryFactory<Z, D, ResultSet>(this.getProxyFactory().locatorsUpdateCopy()); 085 } 086 if (getSQLXMLMethodSet.contains(method)) 087 { 088 return new SQLXMLProxyFactoryFactory<Z, D, ResultSet>(this.getProxyFactory().locatorsUpdateCopy()); 089 } 090 091 return super.getProxyFactoryFactory(object, method, parameters); 092 } 093 094 @Override 095 protected InvocationStrategy getInvocationStrategy(ResultSet resultSet, Method method, Object... parameters) throws SQLException 096 { 097 if (driverReadMethodSet.contains(method)) 098 { 099 return InvocationStrategies.INVOKE_ON_ANY; 100 } 101 102 if (driverWriteMethodSet.contains(method) || method.equals(closeMethod)) 103 { 104 return InvocationStrategies.INVOKE_ON_EXISTING; 105 } 106 107 if (transactionalWriteMethodSet.contains(method)) 108 { 109 return this.getProxyFactory().getTransactionContext().start(InvocationStrategies.TRANSACTION_INVOKE_ON_ALL, this.getProxyFactory().getParentProxy().getConnection()); 110 } 111 112 if (isGetMethod(method)) 113 { 114 return InvocationStrategies.INVOKE_ON_ANY; 115 } 116 117 if (isUpdateMethod(method)) 118 { 119 return InvocationStrategies.INVOKE_ON_EXISTING; 120 } 121 122 return super.getInvocationStrategy(resultSet, method, parameters); 123 } 124 125 @Override 126 protected <R> Invoker<Z, D, ResultSet, R, SQLException> getInvoker(ResultSet results, final Method method, final Object... parameters) throws SQLException 127 { 128 if (isUpdateMethod(method) && (parameters.length > 1)) 129 { 130 return this.getInvoker(method.getParameterTypes()[1], 1, results, method, parameters); 131 } 132 133 return super.getInvoker(results, method, parameters); 134 } 135 136 @Override 137 protected <R> void postInvoke(Invoker<Z, D, ResultSet, R, SQLException> invoker, ResultSet results, Method method, Object... parameters) 138 { 139 if (method.equals(closeMethod)) 140 { 141 this.getProxyFactory().remove(); 142 } 143 144 if (driverWriteMethodSet.contains(method) || isUpdateMethod(method)) 145 { 146 this.getProxyFactory().addInvoker(invoker); 147 } 148 } 149 150 private static boolean isGetMethod(Method method) 151 { 152 Class<?>[] types = method.getParameterTypes(); 153 154 return method.getName().startsWith("get") && (types != null) && (types.length > 0) && (types[0].equals(String.class) || types[0].equals(Integer.TYPE)); 155 } 156 157 private static boolean isUpdateMethod(Method method) 158 { 159 Class<?>[] types = method.getParameterTypes(); 160 161 return method.getName().startsWith("update") && (types != null) && (types.length > 0) && (types[0].equals(String.class) || types[0].equals(Integer.TYPE)); 162 } 163}