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: import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
019: import org.apache.ojb.broker.platforms.PlatformFactory;
020: import org.apache.ojb.broker.platforms.PlatformException;
021: import org.apache.ojb.broker.platforms.Platform;
022:
023: import java.lang.reflect.Method;
024: import java.lang.reflect.InvocationTargetException;
025: import java.math.BigDecimal;
026: import java.sql.Connection;
027: import java.sql.PreparedStatement;
028: import java.sql.SQLException;
029: import java.util.ArrayList;
030:
031: //#ifdef JDK13
032: import java.lang.reflect.InvocationHandler;
033:
034: //#else
035: /*
036: import com.develop.java.lang.reflect.InvocationHandler;
037: */
038: //#endif
039: /**
040: * The implementation of {@link java.reflect.InvocationHandler} which is used
041: * to create dynamic proxy which will implement {@link java.sql.PreparedStatement} and
042: * {@link BatchPreparedStatement}.
043: *
044: * @author Oleg Nitz (<a href="mailto:olegnitz@apache.org">olegnitz@apache.org</a>)
045: */
046: public class PreparedStatementInvocationHandler implements
047: InvocationHandler {
048:
049: private final static Integer ONE = new Integer(1);
050:
051: private static Method ADD_BATCH;
052:
053: private final static Method SET_BIG_DECIMAL;
054:
055: static {
056: Method setBigDecimal = null;
057: try {
058: setBigDecimal = PreparedStatement.class.getMethod(
059: "setBigDecimal", new Class[] { Integer.TYPE,
060: BigDecimal.class });
061: } catch (Exception ex) {
062: // ignore it
063: }
064: SET_BIG_DECIMAL = setBigDecimal;
065: }
066:
067: private final BatchConnection _batchConn;
068:
069: private final String _sql;
070:
071: private ArrayList _methods = new ArrayList();
072:
073: private ArrayList _params = new ArrayList();
074:
075: private Platform m_platform = null;
076:
077: public PreparedStatementInvocationHandler(
078: BatchConnection batchConn, String sql,
079: JdbcConnectionDescriptor jcd) {
080: _batchConn = batchConn;
081: _sql = sql;
082: m_platform = PlatformFactory.getPlatformFor(jcd);
083: try {
084: ADD_BATCH = m_platform.getClass().getMethod("addBatch",
085: new Class[] { PreparedStatement.class });
086: } catch (NoSuchMethodException e) {
087: /**
088: * should never happen
089: */
090: ADD_BATCH = null;
091: } catch (SecurityException e) {
092: // ignore it
093: }
094: }
095:
096: public Object invoke(Object proxy, Method method, Object[] args)
097: throws Throwable {
098: String name = method.getName();
099: if (name.equals("executeUpdate")) {
100: _methods.add(ADD_BATCH);
101: _params.add(null);
102: _batchConn.nextExecuted(_sql);
103: return ONE;
104: } else if (name.equals("doExecute")) {
105: doExecute((Connection) args[0]);
106: } else if (name.startsWith("set")) {
107: // workaround for the bug in Sybase jConnect JDBC driver
108: if (name.equals("setLong")) {
109: method = SET_BIG_DECIMAL;
110: args[1] = BigDecimal.valueOf(((Long) args[1])
111: .longValue());
112: }
113: _methods.add(method);
114: _params.add(args);
115: }
116: return null;
117: }
118:
119: /**
120: * This method performs database modification at the very and of transaction.
121: */
122: private void doExecute(Connection conn) throws SQLException {
123: PreparedStatement stmt;
124: int size;
125:
126: size = _methods.size();
127: if (size == 0) {
128: return;
129: }
130: stmt = conn.prepareStatement(_sql);
131: try {
132: m_platform.afterStatementCreate(stmt);
133: } catch (PlatformException e) {
134: if (e.getCause() instanceof SQLException) {
135: throw (SQLException) e.getCause();
136: } else {
137: throw new SQLException(e.getMessage());
138: }
139: }
140: try {
141: m_platform.beforeBatch(stmt);
142: } catch (PlatformException e) {
143: if (e.getCause() instanceof SQLException) {
144: throw (SQLException) e.getCause();
145: } else {
146: throw new SQLException(e.getMessage());
147: }
148: }
149: try {
150: for (int i = 0; i < size; i++) {
151: Method method = (Method) _methods.get(i);
152: try {
153: if (method.equals(ADD_BATCH)) {
154: /**
155: * we invoke on the platform and pass the stmt as an arg.
156: */
157: m_platform.addBatch(stmt);
158: } else {
159: method.invoke(stmt, (Object[]) _params.get(i));
160: }
161: } catch (IllegalArgumentException ex) {
162: StringBuffer buffer = generateExceptionMessage(i,
163: stmt, ex);
164: throw new SQLException(buffer.toString());
165: } catch (IllegalAccessException ex) {
166: StringBuffer buffer = generateExceptionMessage(i,
167: stmt, ex);
168: throw new SQLException(buffer.toString());
169: } catch (InvocationTargetException ex) {
170: Throwable th = ex.getTargetException();
171:
172: if (th == null) {
173: th = ex;
174: }
175: if (th instanceof SQLException) {
176: throw ((SQLException) th);
177: } else {
178: throw new SQLException(th.toString());
179: }
180: } catch (PlatformException e) {
181: throw new SQLException(e.toString());
182: }
183: }
184: try {
185: /**
186: * this will call the platform specific call
187: */
188: m_platform.executeBatch(stmt);
189: } catch (PlatformException e) {
190: if (e.getCause() instanceof SQLException) {
191: throw (SQLException) e.getCause();
192: } else {
193: throw new SQLException(e.getMessage());
194: }
195: }
196:
197: } finally {
198: stmt.close();
199: _methods.clear();
200: _params.clear();
201: }
202: }
203:
204: private StringBuffer generateExceptionMessage(int i,
205: PreparedStatement stmt, Exception ex) {
206: StringBuffer buffer = new StringBuffer();
207: buffer.append("Method of type: ");
208: buffer.append(_methods.get(i));
209: buffer.append(" invoking on instance: ");
210: if ((_methods.get(i)).equals(ADD_BATCH))
211: buffer.append(m_platform);
212: else
213: buffer.append(stmt);
214: buffer.append(" with parameters: ");
215: if ((_methods.get(i)).equals(ADD_BATCH))
216: buffer.append(stmt);
217: else
218: buffer.append(_params.get(i));
219: buffer.append(" with root: ");
220: buffer.append(ex.toString());
221: return buffer;
222: }
223: }
|