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