/* $Id: JDBCConnection.java 988245 2010-08-23 18:39:35Z kwright $ */ /** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.manifoldcf.jdbc; import org.apache.manifoldcf.core.interfaces.*; import org.apache.manifoldcf.core.database.*; import org.apache.manifoldcf.core.jdbcpool.*; import org.apache.manifoldcf.agents.interfaces.*; import java.nio.charset.StandardCharsets; import java.sql.*; import javax.naming.*; import javax.sql.*; import java.io.*; import java.util.*; /** This object describes a connection to a particular JDBC instance. */ public class JDBCConnection { public static final String _rcsid = "@(#)$Id: JDBCConnection.java 988245 2010-08-23 18:39:35Z kwright $"; protected String jdbcProvider = null; protected boolean useName; protected String driverString = null; protected String userName = null; protected String password = null; /** Constructor. */ public JDBCConnection(String jdbcProvider, boolean useName, String host, String databaseName, String rawDriverString, String userName, String password) throws ManifoldCFException { this.jdbcProvider = jdbcProvider; this.useName = useName; this.driverString = JDBCConnectionFactory.getJDBCDriverString(jdbcProvider, host, databaseName, rawDriverString); this.userName = userName; this.password = password; } protected static IDynamicResultRow readNextResultRowViaThread(ResultSet rs, ResultSetMetaData rsmd, String[] resultCols) throws ManifoldCFException, ServiceInterruption { NextResultRowThread t = new NextResultRowThread(rs,rsmd,resultCols); try { t.start(); return t.finishUp(); } catch (InterruptedException e) { t.interrupt(); throw new ManifoldCFException("Interrupted: "+e.getMessage(),e,ManifoldCFException.INTERRUPTED); } } protected static class NextResultRowThread extends Thread { protected ResultSet rs; protected ResultSetMetaData rsmd; protected String[] resultCols; protected Throwable exception = null; protected IDynamicResultRow response = null; public NextResultRowThread(ResultSet rs, ResultSetMetaData rsmd, String[] resultCols) { super(); setDaemon(true); this.rs = rs; this.rsmd = rsmd; this.resultCols = resultCols; } public void run() { try { response = readNextResultRow(rs,rsmd,resultCols); } catch (Throwable e) { this.exception = e; } } public IDynamicResultRow finishUp() throws ManifoldCFException, ServiceInterruption, InterruptedException { join(); Throwable thr = exception; if (thr != null) { if (thr instanceof java.sql.SQLException) throw new ManifoldCFException("Error fetching next JDBC result row: "+thr.getMessage(),thr); else if (thr instanceof ManifoldCFException) throw (ManifoldCFException)thr; else if (thr instanceof ServiceInterruption) throw (ServiceInterruption)thr; else if (thr instanceof RuntimeException) throw (RuntimeException)thr; else throw (Error)thr; } return response; } } protected static IDynamicResultRow readNextResultRow(ResultSet rs, ResultSetMetaData rsmd, String[] resultCols) throws ManifoldCFException, ServiceInterruption { try { if (rs.next()) { return readResultRow(rs,rsmd,resultCols); } return null; } catch (java.sql.SQLException e) { throw new ManifoldCFException("Result set error: "+e.getMessage(),e); } } protected static void closeResultset(ResultSet rs) throws ManifoldCFException, ServiceInterruption { try { rs.close(); } catch (java.sql.SQLException e) { throw new ManifoldCFException("Exception closing resultset: "+e.getMessage(),e); } } protected static void closeStmt(Statement stmt) throws ManifoldCFException, ServiceInterruption { try { stmt.close(); } catch (java.sql.SQLException e) { throw new ManifoldCFException("Exception closing statement: "+e.getMessage(),e); } } protected static void closePS(PreparedStatement ps) throws ManifoldCFException, ServiceInterruption { try { ps.close(); } catch (java.sql.SQLException e) { throw new ManifoldCFException("Exception closing statement: "+e.getMessage(),e); } } /** Test connection. */ public void testConnection() throws ManifoldCFException, ServiceInterruption { TestConnectionThread t = new TestConnectionThread(); try { t.start(); t.finishUp(); } catch (InterruptedException e) { t.interrupt(); throw new ManifoldCFException("Interrupted: "+e.getMessage(),e,ManifoldCFException.INTERRUPTED); } } protected class TestConnectionThread extends Thread { protected Throwable exception = null; public TestConnectionThread() { super(); setDaemon(true); } public void run() { try { WrappedConnection tempConnection = JDBCConnectionFactory.getConnection(jdbcProvider,driverString,userName,password); JDBCConnectionFactory.releaseConnection(tempConnection); } catch (Throwable e) { this.exception = e; } } public void finishUp() throws ManifoldCFException, ServiceInterruption, InterruptedException { join(); Throwable thr = exception; if (thr != null) { if (thr instanceof java.sql.SQLException) throw new ManifoldCFException("Error doing JDBC connection test: "+thr.getMessage(),thr); else if (thr instanceof ManifoldCFException) throw (ManifoldCFException)thr; else if (thr instanceof ServiceInterruption) throw (ServiceInterruption)thr; else if (thr instanceof RuntimeException) throw (RuntimeException)thr; else throw (Error)thr; } } } /** Execute query. */ public IDynamicResultSet executeUncachedQuery(String query, ArrayList params, int maxResults) throws ManifoldCFException, ServiceInterruption { if (params == null) return new JDBCResultSet(query,maxResults); else return new JDBCPSResultSet(query,params,maxResults); } /** Execute operation. */ public void executeOperation(String query, ArrayList params) throws ManifoldCFException, ServiceInterruption { ExecuteOperationThread t = new ExecuteOperationThread(query,params); try { t.start(); t.finishUp(); } catch (InterruptedException e) { t.interrupt(); throw new ManifoldCFException("Interrupted: "+e.getMessage(),e,ManifoldCFException.INTERRUPTED); } } /** Read object as a string */ public static String readAsString(Object o) throws ManifoldCFException { if (o instanceof BinaryInput) { // Convert this input to a string, since mssql can mess us up with the wrong column types here. BinaryInput bi = (BinaryInput)o; try { InputStream is = bi.getStream(); try { InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8); StringBuilder sb = new StringBuilder(); while (true) { int x = reader.read(); if (x == -1) break; sb.append((char)x); } return sb.toString(); } finally { is.close(); } } catch (IOException e) { throw new ManifoldCFException(e.getMessage(),e); } finally { bi.doneWithStream(); } } else if (o instanceof CharacterInput) { CharacterInput ci = (CharacterInput)o; try { Reader reader = ci.getStream(); try { StringBuilder sb = new StringBuilder(); while (true) { int x = reader.read(); if (x == -1) break; sb.append((char)x); } return sb.toString(); } finally { reader.close(); } } catch (IOException e) { throw new ManifoldCFException(e.getMessage(),e); } finally { ci.doneWithStream(); } } else { return o.toString(); } } protected class ExecuteOperationThread extends Thread { protected String query; protected ArrayList params; protected Throwable exception = null; public ExecuteOperationThread(String query, ArrayList params) { super(); setDaemon(true); this.query = query; this.params = params; } public void run() { try { WrappedConnection tempConnection = JDBCConnectionFactory.getConnection(jdbcProvider,driverString,userName,password); try { execute(tempConnection.getConnection(),query,params,false,0,useName); } finally { JDBCConnectionFactory.releaseConnection(tempConnection); } } catch (Throwable e) { this.exception = e; } } public void finishUp() throws ManifoldCFException, ServiceInterruption, InterruptedException { join(); Throwable thr = exception; if (thr != null) { if (thr instanceof java.sql.SQLException) throw new ManifoldCFException("Exception doing connector query '"+query+"': "+thr.getMessage(),thr); else if (thr instanceof ManifoldCFException) throw (ManifoldCFException)thr; else if (thr instanceof ServiceInterruption) throw (ServiceInterruption)thr; else if (thr instanceof RuntimeException) throw (RuntimeException)thr; else throw (Error)thr; } } } /** Run a query. No caching is involved at all at this level. * @param query String the query string * @param maxResults is the maximum number of results to load: -1 if all * @param params ArrayList if params !=null, use preparedStatement */ protected static IResultSet execute(Connection connection, String query, ArrayList params, boolean bResults, int maxResults, boolean useName) throws ManifoldCFException, ServiceInterruption { ResultSet rs; try { if (params==null) { // lightest statement type Statement stmt = connection.createStatement(); try { stmt.execute(query); rs = stmt.getResultSet(); try { // Suck data from resultset if (bResults) return getData(rs,maxResults,useName); return null; } finally { if (rs != null) rs.close(); } } finally { stmt.close(); } } else { PreparedStatement ps = connection.prepareStatement(query); try { loadPS(ps, params); if (bResults) { rs = ps.executeQuery(); try { // Suck data from resultset return getData(rs,maxResults,useName); } finally { if (rs != null) rs.close(); } } else { ps.executeUpdate(); return null; } } finally { ps.close(); cleanupParameters(params); } } } catch (ManifoldCFException e) { throw e; } catch (java.sql.SQLException e) { throw new ManifoldCFException("Exception doing connector query '"+query+"': "+e.getMessage(),e); } } /** Read the current row from the resultset */ protected static IDynamicResultRow readResultRow(ResultSet rs, ResultSetMetaData rsmd, String[] resultCols) throws ManifoldCFException, ServiceInterruption { try { Object value = null; RDynamicRow m = new RDynamicRow(); // We have 'colcount' cols to look thru for (int i = 0; i < resultCols.length; i++) { String key = resultCols[i]; // System.out.println("Key = "+key); int colnum = findColumn(rs,key); if (colnum > -1) { if (isBinaryData(rsmd,colnum)) { InputStream bis = rs.getBinaryStream(colnum); if (bis != null) { try { value = new TempFileInput(bis); } catch (IOException e) { handleIOException(e,"reading binary data"); } } } else if (isBLOB(rsmd,colnum)) { // System.out.println("It's a blob!"); Blob blob = getBLOB(rs,colnum); // Create a tempfileinput object! // Cleanup should happen by the user of the resultset. // System.out.println(" Blob length = "+Long.toString(blob.length())); if (blob != null) { try { value = new TempFileInput(blob.getBinaryStream(),blob.length()); } catch (IOException e) { handleIOException(e,"reading blob"); } } } else if (isCLOB(rsmd,colnum)) { Clob clob = getCLOB(rs,colnum); if (clob != null) { try { value = new TempFileCharacterInput(clob.getCharacterStream(),clob.length()); } catch (IOException e) { handleIOException(e,"reading clob"); } } } else { // System.out.println("It's not a blob"); value = getObject(rs,rsmd,colnum); } } if (value != null) m.put(key, value); } return m; } catch (java.sql.SQLException e) { throw new ManifoldCFException("Resultset error: "+e.getMessage(),e); } } protected static void handleIOException(IOException e, String context) throws ManifoldCFException { if (e instanceof InterruptedIOException) throw new ManifoldCFException(e.getMessage(),e,ManifoldCFException.INTERRUPTED); throw new ManifoldCFException("IO exception while "+context+": "+e.getMessage(),e); } protected static String[] readColumnNames(ResultSetMetaData rsmd, boolean useName) throws ManifoldCFException, ServiceInterruption { try { String[] resultCols; if (rsmd != null) { int colcount = rsmd.getColumnCount(); resultCols = new String[colcount]; for (int i = 0; i < colcount; i++) { String name; if (useName) name = rsmd.getColumnName(i+1); else name = rsmd.getColumnLabel(i+1); resultCols[i] = name; } } else resultCols = new String[0]; return resultCols; } catch (java.sql.SQLException e) { throw new ManifoldCFException("Sql exception reading column names: "+e.getMessage(),e); } } // Read data from a resultset protected static IResultSet getData(ResultSet rs, int maxResults, boolean useName) throws ManifoldCFException, ServiceInterruption { try { RSet results = new RSet(); // might be empty but not an error if (rs != null) { // Optionally we're going to suck the data // out of the db and return it in a // readonly structure ResultSetMetaData rsmd = rs.getMetaData(); String[] resultCols = readColumnNames(rsmd, useName); if (resultCols.length == 0) { // This is an error situation; if a result with no columns is // necessary, bResults must be false!!! throw new ManifoldCFException("Empty query, no columns returned",ManifoldCFException.GENERAL_ERROR); } while (rs.next() && (maxResults == -1 || maxResults > 0)) { IResultRow m = readResultRow(rs,rsmd,resultCols); if (maxResults != -1) maxResults--; results.addRow(m); } } return results; } catch (java.sql.SQLException e) { throw new ManifoldCFException("Resultset error: "+e.getMessage(),e); } } // pass params to preparedStatement protected static void loadPS(PreparedStatement ps, ArrayList data) throws java.sql.SQLException, ManifoldCFException { if (data!=null) { for (int i = 0; i < data.size(); i++) { // If the input type is a string, then set it as such. // Otherwise, if it's an input stream, we make a blob out of it. Object x = data.get(i); if (x instanceof String) { String value = (String)x; // letting database do lame conversion! ps.setString(i+1, value); } else if (x instanceof BinaryInput) { BinaryInput value = (BinaryInput)x; ps.setBinaryStream(i+1,value.getStream(),value.getLength()); // Hopefully with the introduction of CharacterInput below, this hackery is no longer needed. // System.out.println("Blob length on write = "+Long.toString(value.getLength())); // The oracle driver does a binary conversion to base 64 when writing data // into a clob column using a binary stream operator. Since at this // point there is no way to distinguish the two, and since our tests use CLOB, // this code doesn't work for them. // So, for now, use the ascii stream method. //ps.setAsciiStream(i+1,value.getStream(),value.getLength()); } else if (x instanceof CharacterInput) { CharacterInput value = (CharacterInput)x; ps.setCharacterStream(i+1,value.getStream(),value.getCharacterLength()); } else if (x instanceof java.util.Date) { ps.setDate(i+1,new java.sql.Date(((java.util.Date)x).getTime())); } else if (x instanceof Long) { ps.setLong(i+1,((Long)x).longValue()); } else if (x instanceof TimeMarker) { ps.setTimestamp(i+1,new java.sql.Timestamp(((Long)x).longValue())); } else if (x instanceof Double) { ps.setDouble(i+1,((Double)x).doubleValue()); } else if (x instanceof Integer) { ps.setInt(i+1,((Integer)x).intValue()); } else if (x instanceof Float) { ps.setFloat(i+1,((Float)x).floatValue()); } else throw new ManifoldCFException("Unknown data type: "+x.getClass().getName()); } } } /** Permanently discard database object. */ protected static void discardDatabaseObject(Object x) throws ManifoldCFException { if (x instanceof PersistentDatabaseObject) { PersistentDatabaseObject value = (PersistentDatabaseObject)x; value.discard(); } } /** Call this method on every parameter or result object, when we're done with it, if it's possible that the object is a BLOB * or CLOB. */ protected static void cleanupDatabaseObject(Object x) throws ManifoldCFException { if (x instanceof PersistentDatabaseObject) { PersistentDatabaseObject value = (PersistentDatabaseObject)x; value.doneWithStream(); } } /** Clean up parameters after query has been triggered. */ protected static void cleanupParameters(ArrayList data) throws ManifoldCFException { if (data != null) { for (Object x : data) { cleanupDatabaseObject(x); } } } protected static int findColumn(ResultSet rs, String name) throws ManifoldCFException, ServiceInterruption { try { return rs.findColumn(name); } catch (java.sql.SQLException e) { return -1; } } protected static Blob getBLOB(ResultSet rs, int col) throws ManifoldCFException, ServiceInterruption { try { return rs.getBlob(col); } catch (java.sql.SQLException sqle) { throw new ManifoldCFException("Error in getBlob("+col+"): "+sqle.getMessage(),sqle,ManifoldCFException.DATABASE_ERROR); } } protected static Clob getCLOB(ResultSet rs, int col) throws ManifoldCFException, ServiceInterruption { try { return rs.getClob(col); } catch (java.sql.SQLException sqle) { throw new ManifoldCFException("Error in getClob("+col+"): "+sqle.getMessage(),sqle,ManifoldCFException.DATABASE_ERROR); } } protected static boolean isBLOB(ResultSetMetaData rsmd, int col) throws ManifoldCFException, ServiceInterruption { try { int type = rsmd.getColumnType(col); return (type == java.sql.Types.BLOB); } catch (java.sql.SQLException sqle) { throw new ManifoldCFException("Error in isBlob("+col+"): "+sqle.getMessage(),sqle,ManifoldCFException.DATABASE_ERROR); } } protected static boolean isBinaryData(ResultSetMetaData rsmd, int col) throws ManifoldCFException, ServiceInterruption { try { int type = rsmd.getColumnType(col); return (type == java.sql.Types.VARBINARY || type == java.sql.Types.BINARY || type == java.sql.Types.LONGVARBINARY); } catch (java.sql.SQLException sqle) { throw new ManifoldCFException("Error in isBinaryData("+col+"): "+sqle.getMessage(),sqle,ManifoldCFException.DATABASE_ERROR); } } protected static boolean isCLOB(ResultSetMetaData rsmd, int col) throws ManifoldCFException, ServiceInterruption { try { int type = rsmd.getColumnType(col); return (type == java.sql.Types.CLOB || type == java.sql.Types.LONGVARCHAR); } catch (java.sql.SQLException sqle) { throw new ManifoldCFException("Error in isClob("+col+"): "+sqle.getMessage(),sqle,ManifoldCFException.DATABASE_ERROR); } } protected static Object getObject(ResultSet rs, ResultSetMetaData rsmd, int col) throws ManifoldCFException, ServiceInterruption { Object result = null; try { Timestamp timestamp; java.sql.Date date; Clob clob; String resultString; switch (rsmd.getColumnType(col)) { case java.sql.Types.CHAR : if ((resultString = rs.getString(col)) != null) { if (rsmd.getColumnDisplaySize(col) < resultString.length()) { result = resultString.substring(0,rsmd.getColumnDisplaySize(col)); } else result = resultString; } break; case java.sql.Types.CLOB : if ((clob = rs.getClob(col)) != null) { result = clob.getSubString(1, (int) clob.length()); } break; case java.sql.Types.BIGINT : long l = rs.getLong(col); if (!rs.wasNull()) result = new Long(l); break; case java.sql.Types.INTEGER : int i = rs.getInt(col); if (!rs.wasNull()) result = new Integer(i); break; case java.sql.Types.REAL : case java.sql.Types.FLOAT : float f = rs.getFloat(col); if (!rs.wasNull()) result = new Float(f); break; case java.sql.Types.DOUBLE : double d = rs.getDouble(col); if (!rs.wasNull()) result = new Double(d); break; case java.sql.Types.DATE : if ((date = rs.getDate(col)) != null) { result = new java.util.Date(date.getTime()); } break; case java.sql.Types.TIMESTAMP : if ((timestamp = rs.getTimestamp(col)) != null) { result = new TimeMarker(timestamp.getTime()); } break; case java.sql.Types.BLOB: case java.sql.Types.VARBINARY: case java.sql.Types.BINARY: case java.sql.Types.LONGVARBINARY: throw new ManifoldCFException("Binary type is not a string, column = " + col,ManifoldCFException.GENERAL_ERROR); //break default : result = rs.getString(col); break; } if (rs.wasNull()) { result = null; } } catch (java.sql.SQLException e) { throw new ManifoldCFException("Exception in getString(): "+e.getMessage(),e,ManifoldCFException.DATABASE_ERROR); } return result; } protected class JDBCResultSet implements IDynamicResultSet { protected WrappedConnection connection; protected Statement stmt; protected ResultSet rs; protected ResultSetMetaData rsmd; protected String[] resultCols; protected int maxResults; /** Constructor */ public JDBCResultSet(String query, int maxResults) throws ManifoldCFException, ServiceInterruption { this.maxResults = maxResults; StatementQueryThread t = new StatementQueryThread(query); try { t.start(); t.finishUp(); connection = t.getConnection(); stmt = t.getStatement(); rs = t.getResultSet(); rsmd = t.getResultSetMetaData(); resultCols = t.getColumnNames(); } catch (InterruptedException e) { t.interrupt(); throw new ManifoldCFException("Interrupted: "+e.getMessage(),e,ManifoldCFException.INTERRUPTED); } } /** Get the next row from the resultset. *@return the immutable row description, or null if there is no such row. */ public IDynamicResultRow getNextRow() throws ManifoldCFException, ServiceInterruption { if (maxResults == -1 || maxResults > 0) { IDynamicResultRow row = readNextResultRowViaThread(rs,rsmd,resultCols); if (row != null && maxResults != -1) maxResults--; return row; } return null; } /** Close this resultset. */ public void close() throws ManifoldCFException, ServiceInterruption { ManifoldCFException rval = null; Error error = null; RuntimeException rtException = null; if (rs != null) { try { closeResultset(rs); } catch (ManifoldCFException e) { if (rval == null || e.getErrorCode() == ManifoldCFException.INTERRUPTED) rval = e; } catch (Error e) { error = e; } catch (RuntimeException e) { rtException = e; } finally { rs = null; } } if (stmt != null) { try { closeStmt(stmt); } catch (ManifoldCFException e) { if (rval == null || e.getErrorCode() == ManifoldCFException.INTERRUPTED) rval = e; } catch (Error e) { error = e; } catch (RuntimeException e) { rtException = e; } finally { stmt = null; } } if (connection != null) { try { JDBCConnectionFactory.releaseConnection(connection); } catch (Error e) { error = e; } catch (RuntimeException e) { rtException = e; } finally { connection = null; } } if (error != null) throw error; if (rtException != null) throw rtException; if (rval != null) throw rval; } } protected class StatementQueryThread extends Thread { protected String query; protected Throwable exception = null; protected WrappedConnection connection = null; protected Statement stmt = null; protected ResultSet rs = null; protected ResultSetMetaData rsmd = null; protected String[] resultCols = null; public StatementQueryThread(String query) { super(); setDaemon(true); this.query = query; } public void run() { try { connection = JDBCConnectionFactory.getConnection(jdbcProvider,driverString,userName,password); // lightest statement type stmt = connection.getConnection().createStatement(); stmt.execute(query); rs = stmt.getResultSet(); rsmd = rs.getMetaData(); resultCols = readColumnNames(rsmd,useName); } catch (Throwable e) { this.exception = e; if (rs != null) { try { closeResultset(rs); } catch (ManifoldCFException e2) { if (e2.getErrorCode() == ManifoldCFException.INTERRUPTED) this.exception = e2; // Ignore } catch (Throwable e2) { // We already have an exception to report. // Eat any other exceptions from closing } finally { rs = null; } } if (stmt != null) { try { closeStmt(stmt); } catch (ManifoldCFException e2) { if (e2.getErrorCode() == ManifoldCFException.INTERRUPTED) this.exception = e2; // Ignore } catch (Throwable e2) { // We already have an exception to report. // Eat any other exceptions from closing statements } finally { stmt = null; } } if (connection != null) { JDBCConnectionFactory.releaseConnection(connection); connection = null; } } } public void finishUp() throws ManifoldCFException, ServiceInterruption, InterruptedException { join(); Throwable thr = exception; if (thr != null) { if (thr instanceof java.sql.SQLException) throw new ManifoldCFException("Exception doing connector query '"+query+"': "+thr.getMessage(),thr); else if (thr instanceof ManifoldCFException) throw (ManifoldCFException)thr; else if (thr instanceof ServiceInterruption) throw (ServiceInterruption)thr; else if (thr instanceof RuntimeException) throw (RuntimeException)thr; else throw (Error)thr; } } public WrappedConnection getConnection() { return connection; } public Statement getStatement() { return stmt; } public ResultSet getResultSet() { return rs; } public ResultSetMetaData getResultSetMetaData() { return rsmd; } public String[] getColumnNames() { return resultCols; } } protected class JDBCPSResultSet implements IDynamicResultSet { protected WrappedConnection connection; protected PreparedStatement ps; protected ResultSet rs; protected ResultSetMetaData rsmd; protected String[] resultCols; protected int maxResults; protected ArrayList params; /** Constructor */ public JDBCPSResultSet(String query, ArrayList params, int maxResults) throws ManifoldCFException, ServiceInterruption { this.maxResults = maxResults; this.params = params; PreparedStatementQueryThread t = new PreparedStatementQueryThread(query,params); try { t.start(); t.finishUp(); connection = t.getConnection(); ps = t.getPreparedStatement(); rs = t.getResultSet(); rsmd = t.getResultSetMetaData(); resultCols = t.getColumnNames(); } catch (InterruptedException e) { cleanupParameters(params); t.interrupt(); throw new ManifoldCFException("Interrupted: "+e.getMessage(),e,ManifoldCFException.INTERRUPTED); } } /** Get the next row from the resultset. *@return the immutable row description, or null if there is no such row. */ public IDynamicResultRow getNextRow() throws ManifoldCFException, ServiceInterruption { if (maxResults == -1 || maxResults > 0) { IDynamicResultRow row = readNextResultRowViaThread(rs,rsmd,resultCols); if (row != null && maxResults != -1) maxResults--; return row; } return null; } /** Close this resultset. */ public void close() throws ManifoldCFException, ServiceInterruption { ManifoldCFException rval = null; Error error = null; RuntimeException rtException = null; if (rs != null) { try { closeResultset(rs); } catch (ServiceInterruption e) { } catch (ManifoldCFException e) { if (rval == null || e.getErrorCode() == ManifoldCFException.INTERRUPTED) rval = e; } catch (Error e) { error = e; } catch (RuntimeException e) { rtException = e; } finally { rs = null; } } if (ps != null) { try { closePS(ps); } catch (ServiceInterruption e) { } catch (ManifoldCFException e) { if (rval == null || e.getErrorCode() == ManifoldCFException.INTERRUPTED) rval = e; } catch (Error e) { error = e; } catch (RuntimeException e) { rtException = e; } finally { ps = null; } } if (connection != null) { try { JDBCConnectionFactory.releaseConnection(connection); } catch (Error e) { error = e; } catch (RuntimeException e) { rtException = e; } finally { connection = null; } } if (params != null) { try { cleanupParameters(params); } catch (ManifoldCFException e) { if (rval == null || e.getErrorCode() == ManifoldCFException.INTERRUPTED) rval = e; } catch (Error e) { error = e; } catch (RuntimeException e) { rtException = e; } finally { params = null; } } if (error != null) throw error; if (rtException != null) throw rtException; if (rval != null) throw rval; } } protected class PreparedStatementQueryThread extends Thread { protected ArrayList params; protected String query; protected WrappedConnection connection = null; protected Throwable exception = null; protected PreparedStatement ps = null; protected ResultSet rs = null; protected ResultSetMetaData rsmd = null; protected String[] resultCols = null; public PreparedStatementQueryThread(String query, ArrayList params) { super(); setDaemon(true); this.query = query; this.params = params; } public void run() { try { connection = JDBCConnectionFactory.getConnection(jdbcProvider,driverString,userName,password); ps = connection.getConnection().prepareStatement(query); loadPS(ps, params); rs = ps.executeQuery(); rsmd = rs.getMetaData(); resultCols = readColumnNames(rsmd,useName); } catch (Throwable e) { this.exception = e; if (rs != null) { try { closeResultset(rs); } catch (ManifoldCFException e2) { if (e2.getErrorCode() == ManifoldCFException.INTERRUPTED) this.exception = e2; } catch (Throwable e2) { } finally { rs = null; } } if (ps != null) { try { closePS(ps); } catch (ManifoldCFException e2) { if (e2.getErrorCode() == ManifoldCFException.INTERRUPTED) this.exception = e2; } catch (Throwable e2) { } finally { ps = null; } } if (connection != null) { JDBCConnectionFactory.releaseConnection(connection); connection = null; } } } public void finishUp() throws ManifoldCFException, ServiceInterruption, InterruptedException { join(); Throwable thr = exception; if (thr != null) { // Cleanup of parameters happens even if exception doing query cleanupParameters(params); if (thr instanceof java.sql.SQLException) throw new ManifoldCFException("Exception doing connector query '"+query+"': "+thr.getMessage(),thr); else if (thr instanceof ManifoldCFException) throw (ManifoldCFException)thr; else if (thr instanceof ServiceInterruption) throw (ServiceInterruption)thr; else if (thr instanceof RuntimeException) throw (RuntimeException)thr; else throw (Error)thr; } } public WrappedConnection getConnection() { return connection; } public PreparedStatement getPreparedStatement() { return ps; } public ResultSet getResultSet() { return rs; } public ResultSetMetaData getResultSetMetaData() { return rsmd; } public String[] getColumnNames() { return resultCols; } } /** Dynamic result row implementation */ protected static class RDynamicRow extends RRow implements IDynamicResultRow { public RDynamicRow() { super(); } /** Close this resultrow. */ public void close() throws ManifoldCFException { // Discard everything permanently from the row Iterator<String> columns = getColumns(); while (columns.hasNext()) { String column = columns.next(); Object o = getValue(column); discardDatabaseObject(o); } } } }