View Javadoc

1   package org.apache.ojb.broker.util.batch;
2   
3   /* Copyright 2002-2005 The Apache Software Foundation
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  
19  import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
20  import org.apache.ojb.broker.platforms.PlatformFactory;
21  import org.apache.ojb.broker.platforms.PlatformException;
22  import org.apache.ojb.broker.platforms.Platform;
23  
24  import java.lang.reflect.Method;
25  import java.lang.reflect.InvocationTargetException;
26  import java.math.BigDecimal;
27  import java.sql.Connection;
28  import java.sql.PreparedStatement;
29  import java.sql.SQLException;
30  import java.util.ArrayList;
31  
32  //#ifdef JDK13
33  import java.lang.reflect.InvocationHandler;
34  //#else
35  /*
36  import com.develop.java.lang.reflect.InvocationHandler;
37  */
38  //#endif
39  
40  /**
41   * The implementation of {@link java.reflect.InvocationHandler} which is used
42   * to create dynamic proxy which will implement {@link java.sql.PreparedStatement} and
43   * {@link BatchPreparedStatement}.
44   *
45   * @author Oleg Nitz (<a href="mailto:olegnitz@apache.org">olegnitz@apache.org</a>)
46   */
47  public class PreparedStatementInvocationHandler implements InvocationHandler
48  {
49  
50      private final static Integer ONE = new Integer(1);
51  
52      private static Method ADD_BATCH;
53  
54      private final static Method SET_BIG_DECIMAL;
55  
56      static
57      {
58          Method setBigDecimal = null;
59          try
60          {
61              setBigDecimal = PreparedStatement.class.getMethod("setBigDecimal",
62                                                                new Class[] {Integer.TYPE, BigDecimal.class});
63          }
64          catch ( Exception ex )
65          {
66              // ignore it
67          }
68          SET_BIG_DECIMAL = setBigDecimal;
69      }
70  
71      private final BatchConnection _batchConn;
72  
73      private final String _sql;
74  
75      private ArrayList _methods = new ArrayList();
76  
77      private ArrayList _params = new ArrayList();
78  
79      private Platform m_platform = null;
80  
81      public PreparedStatementInvocationHandler(BatchConnection batchConn, String sql, JdbcConnectionDescriptor jcd)
82      {
83          _batchConn = batchConn;
84          _sql = sql;
85          m_platform = PlatformFactory.getPlatformFor(jcd);
86          try
87          {
88              ADD_BATCH = m_platform.getClass().getMethod("addBatch",new Class[]{PreparedStatement.class});
89          }
90          catch ( NoSuchMethodException e )
91          {
92              /**
93               * should never happen
94               */
95              ADD_BATCH = null;
96          }
97          catch ( SecurityException e )
98          {
99              // ignore it
100         }
101     }
102 
103     public Object invoke(Object proxy, Method method, Object[] args)
104     throws Throwable
105     {
106         String name = method.getName();
107         if ( name.equals("executeUpdate") )
108         {
109             _methods.add(ADD_BATCH);
110             _params.add(null);
111             _batchConn.nextExecuted(_sql);
112             return ONE;
113         }
114         else if ( name.equals("doExecute") )
115         {
116             doExecute((Connection) args[0]);
117         }
118         else if ( name.startsWith("set") )
119         {
120             // workaround for the bug in Sybase jConnect JDBC driver
121             if ( name.equals("setLong") )
122             {
123                 method = SET_BIG_DECIMAL;
124                 args[1] = BigDecimal.valueOf(((Long) args[1]).longValue());
125             }
126             _methods.add(method);
127             _params.add(args);
128         }
129         return null;
130     }
131 
132     /**
133      * This method performs database modification at the very and of transaction.
134      */
135     private void doExecute(Connection conn) throws SQLException
136     {
137         PreparedStatement stmt;
138         int size;
139 
140         size = _methods.size();
141         if ( size == 0 )
142         {
143             return;
144         }
145         stmt = conn.prepareStatement(_sql);
146         try
147         {
148             m_platform.afterStatementCreate(stmt);
149         }
150         catch ( PlatformException e )
151         {
152             if ( e.getCause() instanceof SQLException )
153             {
154                 throw (SQLException)e.getCause();
155             }
156             else
157             {
158                 throw new SQLException(e.getMessage());
159             }
160         }
161         try
162         {
163             m_platform.beforeBatch(stmt);
164         }
165         catch ( PlatformException e )
166         {
167             if ( e.getCause() instanceof SQLException )
168             {
169                 throw (SQLException)e.getCause();
170             }
171             else
172             {
173                 throw new SQLException(e.getMessage());
174             }
175         }
176         try
177         {
178             for ( int i = 0; i < size; i++ )
179             {
180                 Method method = (Method) _methods.get(i);
181                 try
182                 {
183                     if ( method.equals(ADD_BATCH) )
184                     {
185                         /**
186                          * we invoke on the platform and pass the stmt as an arg.
187                          */
188                         m_platform.addBatch(stmt);
189                     }
190                     else
191                     {
192                         method.invoke(stmt, (Object[]) _params.get(i));
193                     }
194                 }
195                 catch (IllegalArgumentException ex)
196                 {
197 					StringBuffer buffer = generateExceptionMessage(i, stmt, ex);
198 					throw new SQLException(buffer.toString());
199                 }
200                 catch ( IllegalAccessException ex )
201                 {
202 					StringBuffer buffer = generateExceptionMessage(i, stmt, ex);
203                     throw new SQLException(buffer.toString());
204                 }
205                 catch ( InvocationTargetException ex )
206                 {
207                     Throwable th = ex.getTargetException();
208 
209                     if ( th == null )
210                     {
211                         th = ex;
212                     }
213                     if ( th instanceof SQLException )
214                     {
215                         throw ((SQLException) th);
216                     }
217                     else
218                     {
219                         throw new SQLException(th.toString());
220                     }
221                 }
222 				catch (PlatformException e)
223 				{
224 					throw new SQLException(e.toString());
225 				}
226             }
227             try
228             {
229                 /**
230                  * this will call the platform specific call
231                  */
232                 m_platform.executeBatch(stmt);
233             }
234             catch ( PlatformException e )
235             {
236                 if ( e.getCause() instanceof SQLException )
237                 {
238                     throw (SQLException)e.getCause();
239                 }
240                 else
241                 {
242                     throw new SQLException(e.getMessage());
243                 }
244             }
245 
246         }
247         finally
248         {
249             stmt.close();
250             _methods.clear();
251             _params.clear();
252         }
253     }
254     
255     private StringBuffer generateExceptionMessage(int i, PreparedStatement stmt, Exception ex)
256 	{
257 		StringBuffer buffer = new StringBuffer();
258 		buffer.append("Method of type: ");
259 		buffer.append(_methods.get(i));
260 		buffer.append(" invoking on instance: ");
261 		if (( _methods.get(i)).equals(ADD_BATCH))
262 			buffer.append(m_platform);
263 		else
264 			buffer.append(stmt);
265 		buffer.append(" with parameters: ");
266 		if ((_methods.get(i)).equals(ADD_BATCH))
267 			buffer.append(stmt);
268 		else
269 			buffer.append(_params.get(i));
270 		buffer.append(" with root: ");
271 		buffer.append(ex.toString());
272 		return buffer;
273 	}
274 }
275