001 package org.apache.ojb.broker.util.batch;
002
003 /* Copyright 2002-2005 The Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018
019 import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
020 import org.apache.ojb.broker.platforms.PlatformFactory;
021 import org.apache.ojb.broker.platforms.PlatformException;
022 import org.apache.ojb.broker.platforms.Platform;
023
024 import java.lang.reflect.Method;
025 import java.lang.reflect.InvocationTargetException;
026 import java.math.BigDecimal;
027 import java.sql.Connection;
028 import java.sql.PreparedStatement;
029 import java.sql.SQLException;
030 import java.util.ArrayList;
031
032 //#ifdef JDK13
033 import java.lang.reflect.InvocationHandler;
034 //#else
035 /*
036 import com.develop.java.lang.reflect.InvocationHandler;
037 */
038 //#endif
039
040 /**
041 * The implementation of {@link java.reflect.InvocationHandler} which is used
042 * to create dynamic proxy which will implement {@link java.sql.PreparedStatement} and
043 * {@link BatchPreparedStatement}.
044 *
045 * @author Oleg Nitz (<a href="mailto:olegnitz@apache.org">olegnitz@apache.org</a>)
046 */
047 public class PreparedStatementInvocationHandler implements InvocationHandler
048 {
049
050 private final static Integer ONE = new Integer(1);
051
052 private static Method ADD_BATCH;
053
054 private final static Method SET_BIG_DECIMAL;
055
056 static
057 {
058 Method setBigDecimal = null;
059 try
060 {
061 setBigDecimal = PreparedStatement.class.getMethod("setBigDecimal",
062 new Class[] {Integer.TYPE, BigDecimal.class});
063 }
064 catch ( Exception ex )
065 {
066 // ignore it
067 }
068 SET_BIG_DECIMAL = setBigDecimal;
069 }
070
071 private final BatchConnection _batchConn;
072
073 private final String _sql;
074
075 private ArrayList _methods = new ArrayList();
076
077 private ArrayList _params = new ArrayList();
078
079 private Platform m_platform = null;
080
081 public PreparedStatementInvocationHandler(BatchConnection batchConn, String sql, JdbcConnectionDescriptor jcd)
082 {
083 _batchConn = batchConn;
084 _sql = sql;
085 m_platform = PlatformFactory.getPlatformFor(jcd);
086 try
087 {
088 ADD_BATCH = m_platform.getClass().getMethod("addBatch",new Class[]{PreparedStatement.class});
089 }
090 catch ( NoSuchMethodException e )
091 {
092 /**
093 * should never happen
094 */
095 ADD_BATCH = null;
096 }
097 catch ( SecurityException e )
098 {
099 // 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