506 lines · 21.2 KB · Java
RawDownload
/* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.rowset.internal; import java.sql.*;import javax.sql.*;import javax.naming.*;import java.io.*;import java.lang.reflect.*; import com.sun.rowset.*;import javax.sql.rowset.*;import javax.sql.rowset.spi.*; /** * The facility called by the <code>RIOptimisticProvider</code> object * internally to read data into it.  The calling <code>RowSet</code> object * must have implemented the <code>RowSetInternal</code> interface * and have the standard <code>CachedRowSetReader</code> object set as its * reader. * <P> * This implementation always reads all rows of the data source, * and it assumes that the <code>command</code> property for the caller * is set with a query that is appropriate for execution by a * <code>PreparedStatement</code> object. * <P> * Typically the <code>SyncFactory</code> manages the <code>RowSetReader</code> and * the <code>RowSetWriter</code> implementations using <code>SyncProvider</code> objects. * Standard JDBC RowSet implementations provide an object instance of this * reader by invoking the <code>SyncProvider.getRowSetReader()</code> method. * * @author Jonathan Bruce * @see javax.sql.rowset.spi.SyncProvider * @see javax.sql.rowset.spi.SyncFactory * @see javax.sql.rowset.spi.SyncFactoryException */public class CachedRowSetReader implements RowSetReader, Serializable {     /**     * The field that keeps track of whether the writer associated with     * this <code>CachedRowSetReader</code> object's rowset has been called since     * the rowset was populated.     * <P>     * When this <code>CachedRowSetReader</code> object reads data into     * its rowset, it sets the field <code>writerCalls</code> to 0.     * When the writer associated with the rowset is called to write     * data back to the underlying data source, its <code>writeData</code>     * method calls the method <code>CachedRowSetReader.reset</code>,     * which increments <code>writerCalls</code> and returns <code>true</code>     * if <code>writerCalls</code> is 1. Thus, <code>writerCalls</code> equals     * 1 after the first call to <code>writeData</code> that occurs     * after the rowset has had data read into it.     *     * @serial     */    private int writerCalls = 0;     private boolean userCon = false;     private int startPosition;     private JdbcRowSetResourceBundle resBundle;     public CachedRowSetReader() {        try {                resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle();        } catch(IOException ioe) {            throw new RuntimeException(ioe);        }    }      /**     * Reads data from a data source and populates the given     * <code>RowSet</code> object with that data.     * This method is called by the rowset internally when     * the application invokes the method <code>execute</code>     * to read a new set of rows.     * <P>     * After clearing the rowset of its contents, if any, and setting     * the number of writer calls to <code>0</code>, this reader calls     * its <code>connect</code> method to make     * a connection to the rowset's data source. Depending on which     * of the rowset's properties have been set, the <code>connect</code>     * method will use a <code>DataSource</code> object or the     * <code>DriverManager</code> facility to make a connection to the     * data source.     * <P>     * Once the connection to the data source is made, this reader     * executes the query in the calling <code>CachedRowSet</code> object's     * <code>command</code> property. Then it calls the rowset's     * <code>populate</code> method, which reads data from the     * <code>ResultSet</code> object produced by executing the rowset's     * command. The rowset is then populated with this data.     * <P>     * This method's final act is to close the connection it made, thus     * leaving the rowset disconnected from its data source.     *     * @param caller a <code>RowSet</code> object that has implemented     *               the <code>RowSetInternal</code> interface and had     *               this <code>CachedRowSetReader</code> object set as     *               its reader     * @throws SQLException if there is a database access error, there is a     *         problem making the connection, or the command property has not     *         been set     */    public void readData(RowSetInternal caller) throws SQLException    {        Connection con = null;        try {            CachedRowSet crs = (CachedRowSet)caller;             // Get rid of the current contents of the rowset.             /**             * Checking added to verify whether page size has been set or not.             * If set then do not close the object as certain parameters need             * to be maintained.             */             if(crs.getPageSize() == 0 && crs.size() >0 ) {               // When page size is not set,               // crs.size() will show the total no of rows.               crs.close();            }             writerCalls = 0;             // Get a connection.  This reader assumes that the necessary            // properties have been set on the caller to let it supply a            // connection.            userCon = false;             con = this.connect(caller);             // Check our assumptions.            if (con == null || crs.getCommand() == null)                throw new SQLException(resBundle.handleGetObject("crsreader.connecterr").toString());             try {                con.setTransactionIsolation(crs.getTransactionIsolation());            } catch (Exception ex) {                ;            }            // Use JDBC to read the data.            PreparedStatement pstmt = con.prepareStatement(crs.getCommand());            // Pass any input parameters to JDBC.             decodeParams(caller.getParams(), pstmt);            try {                pstmt.setMaxRows(crs.getMaxRows());                pstmt.setMaxFieldSize(crs.getMaxFieldSize());                pstmt.setEscapeProcessing(crs.getEscapeProcessing());                pstmt.setQueryTimeout(crs.getQueryTimeout());            } catch (Exception ex) {                /*                 * drivers may not support the above - esp. older                 * drivers being used by the bridge..                 */                throw new SQLException(ex.getMessage());            }             if(crs.getCommand().toLowerCase().indexOf("select") != -1) {                // can be (crs.getCommand()).indexOf("select")) == 0                // because we will be getting resultset when                // it may be the case that some false select query with                // select coming in between instead of first.                 // if ((crs.getCommand()).indexOf("?")) does not return -1                // implies a Prepared Statement like query exists.                 ResultSet rs = pstmt.executeQuery();               if(crs.getPageSize() == 0){                      crs.populate(rs);               }               else {                       /**                        * If page size has been set then create a ResultSet object that is scrollable using a                        * PreparedStatement handle.Also call the populate(ResultSet,int) function to populate                        * a page of data as specified by the page size.                        */                       pstmt = con.prepareStatement(crs.getCommand(),ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);                       decodeParams(caller.getParams(), pstmt);                       try {                               pstmt.setMaxRows(crs.getMaxRows());                               pstmt.setMaxFieldSize(crs.getMaxFieldSize());                               pstmt.setEscapeProcessing(crs.getEscapeProcessing());                               pstmt.setQueryTimeout(crs.getQueryTimeout());                           } catch (Exception ex) {                          /*                           * drivers may not support the above - esp. older                           * drivers being used by the bridge..                           */                            throw new SQLException(ex.getMessage());                          }                       rs = pstmt.executeQuery();                       crs.populate(rs,startPosition);               }                rs.close();            } else  {                pstmt.executeUpdate();            }             // Get the data.            pstmt.close();            try {                con.commit();            } catch (SQLException ex) {                ;            }            // only close connections we created...            if (getCloseConnection() == true)                con.close();        }        catch (SQLException ex) {            // Throw an exception if reading fails for any reason.            throw ex;        } finally {            try {                // only close connections we created...                if (con != null && getCloseConnection() == true) {                    try {                        if (!con.getAutoCommit()) {                            con.rollback();                        }                    } catch (Exception dummy) {                        /*                         * not an error condition, we're closing anyway, but                         * we'd like to clean up any locks if we can since                         * it is not clear the connection pool will clean                         * these connections in a timely manner                         */                    }                    con.close();                    con = null;                }            } catch (SQLException e) {                // will get exception if something already went wrong, but don't                // override that exception with this one            }        }    }     /**     * Checks to see if the writer associated with this reader needs     * to reset its state.  The writer will need to initialize its state     * if new contents have been read since the writer was last called.     * This method is called by the writer that was registered with     * this reader when components were being wired together.     *     * @return <code>true</code> if writer associated with this reader needs     *         to reset the values of its fields; <code>false</code> otherwise     * @throws SQLException if an access error occurs     */    public boolean reset() throws SQLException {        writerCalls++;        return writerCalls == 1;    }     /**     * Establishes a connection with the data source for the given     * <code>RowSet</code> object.  If the rowset's <code>dataSourceName</code>     * property has been set, this method uses the JNDI API to retrieve the     * <code>DataSource</code> object that it can use to make the connection.     * If the url, username, and password properties have been set, this     * method uses the <code>DriverManager.getConnection</code> method to     * make the connection.     * <P>     * This method is used internally by the reader and writer associated with     * the calling <code>RowSet</code> object; an application never calls it     * directly.     *     * @param caller a <code>RowSet</code> object that has implemented     *               the <code>RowSetInternal</code> interface and had     *               this <code>CachedRowSetReader</code> object set as     *               its reader     * @return a <code>Connection</code> object that represents a connection     *         to the caller's data source     * @throws SQLException if an access error occurs     */    public Connection connect(RowSetInternal caller) throws SQLException {         // Get a JDBC connection.        if (caller.getConnection() != null) {            // A connection was passed to execute(), so use it.            // As we are using a connection the user gave us we            // won't close it.            userCon = true;            return caller.getConnection();        }        else if (((RowSet)caller).getDataSourceName() != null) {            // Connect using JNDI.            try {                Context ctx = new InitialContext();                DataSource ds = (DataSource)ctx.lookup                    (((RowSet)caller).getDataSourceName());                 // Check for username, password,                // if it exists try getting a Connection handle through them                // else try without these                // else throw SQLException                 if(((RowSet)caller).getUsername() != null) {                     return ds.getConnection(((RowSet)caller).getUsername(),                                            ((RowSet)caller).getPassword());                } else {                     return ds.getConnection();                }            }            catch (javax.naming.NamingException ex) {                SQLException sqlEx = new SQLException(resBundle.handleGetObject("crsreader.connect").toString());                sqlEx.initCause(ex);                throw sqlEx;            }        } else if (((RowSet)caller).getUrl() != null) {            // Connect using the driver manager.            return DriverManager.getConnection(((RowSet)caller).getUrl(),                                               ((RowSet)caller).getUsername(),                                               ((RowSet)caller).getPassword());        }        else {            return null;        }    }     /**     * Sets the parameter placeholders     * in the rowset's command (the given <code>PreparedStatement</code>     * object) with the parameters in the given array.     * This method, called internally by the method     * <code>CachedRowSetReader.readData</code>, reads each parameter, and     * based on its type, determines the correct     * <code>PreparedStatement.setXXX</code> method to use for setting     * that parameter.     *     * @param params an array of parameters to be used with the given     *               <code>PreparedStatement</code> object     * @param pstmt  the <code>PreparedStatement</code> object that is the     *               command for the calling rowset and into which     *               the given parameters are to be set     * @throws SQLException if an access error occurs     */    private void decodeParams(Object[] params,                              PreparedStatement pstmt) throws SQLException {    // There is a corresponding decodeParams in JdbcRowSetImpl    // which does the same as this method. This is a design flaw.    // Update the JdbcRowSetImpl.decodeParams when you update    // this method.     // Adding the same comments to JdbcRowSetImpl.decodeParams.         int arraySize;        Object[] param = null;         for (int i=0; i < params.length; i++) {            if (params[i] instanceof Object[]) {                param = (Object[])params[i];                 if (param.length == 2) {                    if (param[0] == null) {                        pstmt.setNull(i + 1, ((Integer)param[1]).intValue());                        continue;                    }                     if (param[0] instanceof java.sql.Date ||                        param[0] instanceof java.sql.Time ||                        param[0] instanceof java.sql.Timestamp) {                        System.err.println(resBundle.handleGetObject("crsreader.datedetected").toString());                        if (param[1] instanceof java.util.Calendar) {                            System.err.println(resBundle.handleGetObject("crsreader.caldetected").toString());                            pstmt.setDate(i + 1, (java.sql.Date)param[0],                                       (java.util.Calendar)param[1]);                            continue;                        }                        else {                            throw new SQLException(resBundle.handleGetObject("crsreader.paramtype").toString());                        }                    }                     if (param[0] instanceof Reader) {                        pstmt.setCharacterStream(i + 1, (Reader)param[0],                                              ((Integer)param[1]).intValue());                        continue;                    }                     /*                     * What's left should be setObject(int, Object, scale)                     */                    if (param[1] instanceof Integer) {                        pstmt.setObject(i + 1, param[0], ((Integer)param[1]).intValue());                        continue;                    }                 } else if (param.length == 3) {                     if (param[0] == null) {                        pstmt.setNull(i + 1, ((Integer)param[1]).intValue(),                                   (String)param[2]);                        continue;                    }                     if (param[0] instanceof java.io.InputStream) {                        switch (((Integer)param[2]).intValue()) {                        case CachedRowSetImpl.UNICODE_STREAM_PARAM:                            pstmt.setUnicodeStream(i + 1,                                                (java.io.InputStream)param[0],                                                ((Integer)param[1]).intValue());                        case CachedRowSetImpl.BINARY_STREAM_PARAM:                            pstmt.setBinaryStream(i + 1,                                               (java.io.InputStream)param[0],                                               ((Integer)param[1]).intValue());                        case CachedRowSetImpl.ASCII_STREAM_PARAM:                            pstmt.setAsciiStream(i + 1,                                              (java.io.InputStream)param[0],                                              ((Integer)param[1]).intValue());                        default:                            throw new SQLException(resBundle.handleGetObject("crsreader.paramtype").toString());                        }                    }                     /*                     * no point at looking at the first element now;                     * what's left must be the setObject() cases.                     */                    if (param[1] instanceof Integer && param[2] instanceof Integer) {                        pstmt.setObject(i + 1, param[0], ((Integer)param[1]).intValue(),                                     ((Integer)param[2]).intValue());                        continue;                    }                     throw new SQLException(resBundle.handleGetObject("crsreader.paramtype").toString());                 } else {                    // common case - this catches all SQL92 types                    pstmt.setObject(i + 1, params[i]);                    continue;                }            }  else {               // Try to get all the params to be set here               pstmt.setObject(i + 1, params[i]);             }        }    }     /**     * Assists in determining whether the current connection was created by this     * CachedRowSet to ensure incorrect connections are not prematurely terminated.     *     * @return a boolean giving the status of whether the connection has been closed.     */    protected boolean getCloseConnection() {        if (userCon == true)            return false;         return true;    }     /**     *  This sets the start position in the ResultSet from where to begin. This is     * called by the Reader in the CachedRowSetImpl to set the position on the page     * to begin populating from.     * @param pos integer indicating the position in the <code>ResultSet</code> to begin     *        populating from.     */    public void setStartPosition(int pos){        startPosition = pos;    }     private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {        // Default state initialization happens here        ois.defaultReadObject();        // Initialization of  Res Bundle happens here .        try {           resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle();        } catch(IOException ioe) {            throw new RuntimeException(ioe);

Lines 1–500 of 506.