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