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 */
021package net.sf.hajdbc.sql;
022
023import java.lang.reflect.Method;
024import java.lang.reflect.Proxy;
025import java.sql.Clob;
026import java.sql.Connection;
027import java.sql.SQLException;
028import java.sql.Savepoint;
029import java.util.Map;
030import java.util.Set;
031
032import net.sf.hajdbc.Database;
033import net.sf.hajdbc.util.reflect.Methods;
034
035/**
036 * @author Paul Ferraro
037 * @param <D> 
038 * @param <P> 
039 */
040@SuppressWarnings("nls")
041public class ConnectionInvocationHandler<D, P> extends AbstractChildInvocationHandler<D, P, Connection>
042{
043        private static final Set<Method> driverReadMethodSet = Methods.findMethods(Connection.class, "create(ArrayOf|Blob|Clob|NClob|SQLXML|Struct)", "getAutoCommit", "getCatalog", "getClientInfo", "getHoldability", "getTypeMap", "getWarnings", "isClosed", "isReadOnly", "nativeSQL");
044        private static final Set<Method> databaseReadMethodSet = Methods.findMethods(Connection.class, "getTransactionIsolation", "isValid");
045        private static final Set<Method> driverWriteMethodSet = Methods.findMethods(Connection.class, "clearWarnings", "setAutoCommit", "setClientInfo", "setHoldability", "setTypeMap");
046        private static final Set<Method> endTransactionMethodSet = Methods.findMethods(Connection.class, "commit", "rollback");
047        private static final Set<Method> createStatementMethodSet = Methods.findMethods(Connection.class, "createStatement");
048        private static final Set<Method> prepareStatementMethodSet = Methods.findMethods(Connection.class, "prepareStatement");
049        private static final Set<Method> prepareCallMethodSet = Methods.findMethods(Connection.class, "prepareCall");
050        private static final Set<Method> setSavepointMethodSet = Methods.findMethods(Connection.class, "setSavepoint");
051        private static final Set<Method> createClobMethodSet = Methods.findMethods(Connection.class, "createN?Clob");
052        
053        private static final Method getMetaDataMethod = Methods.getMethod(Connection.class, "getMetaData");
054        private static final Method releaseSavepointMethod = Methods.getMethod(Connection.class, "releaseSavepoint", Savepoint.class);
055        private static final Method rollbackSavepointMethod = Methods.getMethod(Connection.class, "rollback", Savepoint.class);
056        private static final Method closeMethod = Methods.getMethod(Connection.class, "close");
057        private static final Method createArrayOfMethod = Methods.findMethod(Connection.class, "createArrayOf");
058        private static final Method createBlobMethod = Methods.findMethod(Connection.class, "createBlob");
059        private static final Method createSQLXMLMethod = Methods.findMethod(Connection.class, "createSQLXML");
060        
061        private TransactionContext<D> transactionContext;
062        
063        /**
064         * @param proxy
065         * @param handler
066         * @param invoker
067         * @param connectionMap
068         * @param transactionContext 
069         * @throws Exception
070         */
071        public ConnectionInvocationHandler(P proxy, SQLProxy<D, P> handler, Invoker<D, P, Connection> invoker, Map<Database<D>, Connection> connectionMap, TransactionContext<D> transactionContext) throws Exception
072        {
073                super(proxy, handler, invoker, Connection.class, connectionMap);
074                
075                this.transactionContext = transactionContext;
076        }
077        
078        /**
079         * @see net.sf.hajdbc.sql.AbstractChildInvocationHandler#getInvocationStrategy(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
080         */
081        @Override
082        protected InvocationStrategy<D, Connection, ?> getInvocationStrategy(Connection connection, Method method, Object[] parameters) throws Exception
083        {
084                if (driverReadMethodSet.contains(method))
085                {
086                        return new DriverReadInvocationStrategy<D, Connection, Object>();
087                }
088                
089                if (databaseReadMethodSet.contains(method))
090                {
091                        return new DatabaseReadInvocationStrategy<D, Connection, Object>();
092                }
093                
094                if (driverWriteMethodSet.contains(method) || method.equals(closeMethod))
095                {
096                        return new DriverWriteInvocationStrategy<D, Connection, Object>();
097                }
098                
099                if (endTransactionMethodSet.contains(method))
100                {
101                        return this.transactionContext.end(new DatabaseWriteInvocationStrategy<D, Connection, Void>(this.cluster.getTransactionalExecutor()));
102                }
103                
104                if (method.equals(rollbackSavepointMethod) || method.equals(releaseSavepointMethod))
105                {
106                        return new DatabaseWriteInvocationStrategy<D, Connection, Void>(this.cluster.getTransactionalExecutor());
107                }
108                
109                if (createStatementMethodSet.contains(method))
110                {
111                        return new StatementInvocationStrategy<D>(connection, this.transactionContext);
112                }
113                
114                if (prepareStatementMethodSet.contains(method))
115                {
116                        return new PreparedStatementInvocationStrategy<D>(this.cluster, connection, this.transactionContext, (String) parameters[0]);
117                }
118                
119                if (prepareCallMethodSet.contains(method))
120                {
121                        return new CallableStatementInvocationStrategy<D>(this.cluster, connection, this.transactionContext);
122                }
123                
124                if (setSavepointMethodSet.contains(method))
125                {
126                        return new SavepointInvocationStrategy<D>(this.cluster, connection);
127                }
128                
129                if (method.equals(getMetaDataMethod))
130                {
131                        return new DatabaseMetaDataInvocationStrategy<D>(connection);
132                }
133                
134                if ((createBlobMethod != null) && method.equals(createBlobMethod))
135                {
136                        return new BlobInvocationStrategy<D, Connection>(this.cluster, connection);
137                }
138                
139                if (createClobMethodSet.contains(method))
140                {
141                        return new ClobInvocationStrategy<D, Connection>(this.cluster, connection, method.getReturnType().asSubclass(Clob.class));
142                }
143                
144                if ((createArrayOfMethod != null) && method.equals(createArrayOfMethod))
145                {
146                        return new ArrayInvocationStrategy<D, Connection>(this.cluster, connection);
147                }
148                
149                if ((createSQLXMLMethod != null) && method.equals(createSQLXMLMethod))
150                {
151                        return new SQLXMLInvocationStrategy<D, Connection>(this.cluster, connection);
152                }
153                
154                return super.getInvocationStrategy(connection, method, parameters);
155        }
156
157        /**
158         * @see net.sf.hajdbc.sql.AbstractChildInvocationHandler#getInvoker(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
159         */
160        @SuppressWarnings("unchecked")
161        @Override
162        protected Invoker<D, Connection, ?> getInvoker(Connection connection, Method method, Object[] parameters) throws Exception
163        {
164                if (method.equals(releaseSavepointMethod))
165                {
166                        final SQLProxy<D, Savepoint> proxy = (SQLProxy) Proxy.getInvocationHandler(parameters[0]);
167                        
168                        return new Invoker<D, Connection, Void>()
169                        {
170                                public Void invoke(Database<D> database, Connection connection) throws SQLException
171                                {
172                                        connection.releaseSavepoint(proxy.getObject(database));
173                                        
174                                        return null;
175                                }                                       
176                        };
177                }
178                
179                if (method.equals(rollbackSavepointMethod))
180                {
181                        final SQLProxy<D, Savepoint> proxy = (SQLProxy) Proxy.getInvocationHandler(parameters[0]);
182                        
183                        return new Invoker<D, Connection, Void>()
184                        {
185                                public Void invoke(Database<D> database, Connection connection) throws SQLException
186                                {
187                                        connection.rollback(proxy.getObject(database));
188                                        
189                                        return null;
190                                }                                       
191                        };
192                }
193                
194                return super.getInvoker(connection, method, parameters);
195        }
196
197        /**
198         * @see net.sf.hajdbc.sql.AbstractChildInvocationHandler#isSQLMethod(java.lang.reflect.Method)
199         */
200        @Override
201        protected boolean isSQLMethod(Method method)
202        {
203                return prepareStatementMethodSet.contains(method);
204        }
205
206        /**
207         * @see net.sf.hajdbc.sql.AbstractChildInvocationHandler#postInvoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
208         */
209        @SuppressWarnings("unchecked")
210        @Override
211        protected void postInvoke(Connection object, Method method, Object[] parameters)
212        {
213                if (method.equals(closeMethod))
214                {
215                        this.transactionContext.close();
216                        
217                        this.getParentProxy().removeChild(this);
218                }
219                else if (method.equals(releaseSavepointMethod))
220                {
221                        SQLProxy<D, Savepoint> proxy = (SQLProxy) Proxy.getInvocationHandler(parameters[0]);
222                        
223                        this.removeChild(proxy);
224                }
225        }
226
227        /**
228         * @see net.sf.hajdbc.sql.AbstractChildInvocationHandler#close(java.lang.Object, java.lang.Object)
229         */
230        @Override
231        protected void close(P parent, Connection connection) throws SQLException
232        {
233                connection.close();
234        }
235
236        /**
237         * @see net.sf.hajdbc.sql.AbstractInvocationHandler#isRecordable(java.lang.reflect.Method)
238         */
239        @Override
240        protected boolean isRecordable(Method method)
241        {
242                return driverWriteMethodSet.contains(method);
243        }
244}