Java tutorial
/* * This file is part of AceQL. * AceQL: Remote JDBC access over HTTP. * Copyright (C) 2015, KawanSoft SAS * (http://www.kawansoft.com). All rights reserved. * * AceQL is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * AceQL 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * Any modifications to this file must keep this entire header * intact. */ package org.kawanfw.sql.jdbc; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; import java.math.BigDecimal; import java.net.URL; import java.sql.Array; import java.sql.Blob; import java.sql.Clob; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.RowId; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.sql.SQLWarning; import java.sql.Statement; import java.sql.Timestamp; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Vector; import java.util.logging.Level; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.kawanfw.commons.api.client.SessionParameters; import org.kawanfw.commons.jdbc.abstracts.AbstractResultSet; import org.kawanfw.commons.util.ClientLogger; import org.kawanfw.commons.util.FrameworkDebug; import org.kawanfw.commons.util.HtmlConverter; import org.kawanfw.commons.util.KeepTempFilePolicyParms; import org.kawanfw.commons.util.Tag; import org.kawanfw.file.api.client.RemoteFile; import org.kawanfw.file.api.client.RemoteInputStream; import org.kawanfw.file.api.client.RemoteSession; import org.kawanfw.file.api.util.client.FilesTransferWithProgress; import org.kawanfw.sql.jdbc.http.JdbcHttpStatementTransfer; import org.kawanfw.sql.jdbc.http.JdbcHttpTransferUtil; import org.kawanfw.sql.jdbc.util.ColPositionBuilder; import org.kawanfw.sql.jdbc.util.FileBackedListRs; import org.kawanfw.sql.jdbc.util.JsonLineDecryptor; import org.kawanfw.sql.jdbc.util.ResultSetFileSplitter; import org.kawanfw.sql.json.StatementHolder; import org.kawanfw.sql.json.no_obfuscation.ResultSetMetaDataHolder; import org.kawanfw.sql.json.no_obfuscation.ResultSetMetaDataHolderTransport; import org.kawanfw.sql.transport.TransportConverter; import org.kawanfw.sql.transport.UrlTransporter; import org.kawanfw.sql.transport.no_obfsucation.ArrayTransporter; import org.kawanfw.sql.transport.no_obfsucation.RowIdTransporter; import org.kawanfw.sql.util.crypto.StatementHolderCrypto; /** * ResultSet Wrapper. <br> * Implements all the ResultSet methods. Usage is exactly the same as a * ResultSet. * */ public class ResultSetHttp extends AbstractResultSet implements ResultSet { private static final String HTML_DECODED = ".html-decoded.txt"; /** Debug flag */ private static boolean DEBUG = FrameworkDebug.isSet(ResultSetHttp.class); /** Universal and clean line separator */ private static String CR_LF = System.getProperty("line.separator"); /** * Map that stores ResultSetMetaData per ResultSet to avoid server * re-contact if programmer calls many times ResultSet.getMetaData() in a * loop instead of reusing the result value, as in: int columnType = * resultSet.getMetaData().getColumnType(columnIndex.intValue()); */ private static Map<ResultSet, ResultSetMetaData> resultSetMetaDataMap = new HashMap<ResultSet, ResultSetMetaData>(); /** The RemoteConnection in use */ private ConnectionHttp connectionHttp = null; /** * The holder that contains the sql order and the list if (parameter type, * parameter value ) for prepared statements **/ private StatementHolder statementHolder = null; /** * The original Statement associated to the Result Set (maybe null for * Metadata calls) */ private Statement statement = null; /** The list of files to delete at close */ private List<File> localFiles = new Vector<File>(); /** The list of remote file to delete at close */ private List<String> remoteFiles = new Vector<String>(); /** The result set type. Defaults to ResultSet.TYPE_FORWARD_ONLY */ private int resultSetType = ResultSet.TYPE_FORWARD_ONLY; /** The result set concurrency. Defaults to CONCUR_READ_ONLY */ private int resultSetConcurrency = ResultSet.CONCUR_READ_ONLY; /** The result set holdability. Defaults to CLOSE_CURSORS_AT_COMMIT */ private int resultSetHoldability = ResultSet.CLOSE_CURSORS_AT_COMMIT; /** The map of (Column Name, Column Position) */ private Map<String, Integer> colPosition = null; /** The List of column values for 1 result set row */ // private List<Map<String, String>> list = null; private List<List<String>> list = null; /** The cursor of the current row */ private int cursor = 0; /** Says if the last accessed value was null */ private boolean wasNull = false; /** * Method name if, we want to to a ResultSet.getMetaData() on the ResultSet * produced by a Connection.getMetaData() */ @SuppressWarnings("unused") private String MetaDataMethodName = null; /** Parms of the MetaDataMethodName */ @SuppressWarnings("unused") private Object[] MetaDataMethodParams = null; /** * The file that contains the MetaData of the Result Set produced by a * getMetaData function */ private File rsMetaDataFileTypeFunction = null; /** * The file that contains the MetaData of the Result Set produced by a * classical/normal statement or preparedStatement */ private File rsMetaDataFileTypeNormal = null; /** * Constructor when building the ResultSet from a getMetaData function. * Allows to use directly a getMetaData() on the ResultSet without a new * call on server * * @param connectionHttp * The Http Connection * @param rsAndMetaDataFile * the result file that contains both the ResultSet and it's * MetaData * @param metaDataMethodName * The method name that produced the result set * @param metaDataMethodParams * The parameters of the method */ public ResultSetHttp(ConnectionHttp connectionHttp, String metaDataMethodName, File rsAndMetaDataFile, Object... metaDataMethodParams) throws SQLException { if (connectionHttp == null) { String message = Tag.PRODUCT_PRODUCT_FAIL + "connectionHttp can not be null!"; throw new SQLException(message, new IOException(message)); } if (rsAndMetaDataFile == null) { String message = Tag.PRODUCT_PRODUCT_FAIL + "Result Set as String can not be null!"; throw new SQLException(message, new IOException(message)); } this.connectionHttp = connectionHttp; this.MetaDataMethodName = metaDataMethodName; this.MetaDataMethodParams = metaDataMethodParams; resultSetType = ResultSet.TYPE_SCROLL_INSENSITIVE; // Split the result file into two files: 1) ResultSet itself 2) // ResultSet.getMetaData ResultSetFileSplitter resultSetFileSplitter = new ResultSetFileSplitter(rsAndMetaDataFile); File rsFile = resultSetFileSplitter.getResultSetFile(); rsMetaDataFileTypeFunction = resultSetFileSplitter.getMetaDataFile(); debug("rsAndMetaDataFile: " + rsAndMetaDataFile); debug("rsFile : " + rsMetaDataFileTypeFunction); debug("rsMetaDataFile : " + rsMetaDataFileTypeFunction); // Build the column position map: ColPositionBuilder colPositionBuilder = new ColPositionBuilder(rsFile, connectionHttp); colPosition = colPositionBuilder.getColPosition(); // Build the List of Strings list = new FileBackedListRs<List<String>>(rsFile, connectionHttp); } /** * Constructor * * @param connectionHttp * The Http Connection * @param statementHolder * The holder that contains the sql order and the list if * (parameter type, parameter value ) for prepared statements. * (May be null for DatabaseMetaData.method calls) * @param statement * @param rsFile * the Result Set as a file: <br> * - one {columnName1=value2, columnName2=value2, ... * columnNamei=valuei} per line * @param connection * the Jdbc Connection * */ public ResultSetHttp(ConnectionHttp connectionHttp, StatementHolder statementHolder, Statement statement, File rsFile) throws SQLException { if (connectionHttp == null) { String message = Tag.PRODUCT_PRODUCT_FAIL + "connectionHttp can not be null!"; throw new SQLException(message, new IOException(message)); } if (rsFile == null) { String message = Tag.PRODUCT_PRODUCT_FAIL + "Result Set as String can not be null!"; throw new SQLException(message, new IOException(message)); } this.connectionHttp = connectionHttp; this.statementHolder = statementHolder; this.statement = statement; if (statementHolder != null) { this.resultSetType = statementHolder.getResultSetType(); this.resultSetConcurrency = statementHolder.getResultSetConcurrency(); this.resultSetHoldability = statementHolder.getResultSetConcurrency(); } // Split the result file into two files: 1) ResultSet itself 2) // ResultSet.getMetaData try { debug("rsFile: " + CR_LF + FileUtils.readFileToString(rsFile)); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } debug("connectionHttp.isJoinResultSetMetaData(): " + connectionHttp.isJoinResultSetMetaData()); if (connectionHttp.isJoinResultSetMetaData()) { ResultSetFileSplitter resultSetFileSplitter = new ResultSetFileSplitter(rsFile); File rsFileOnly = resultSetFileSplitter.getResultSetFile(); rsMetaDataFileTypeNormal = resultSetFileSplitter.getMetaDataFile(); // Build the column position map: ColPositionBuilder colPositionBuilder = new ColPositionBuilder(rsFileOnly, connectionHttp); colPosition = colPositionBuilder.getColPosition(); // Build the List of Strings // list = new FileBackedListOfMaps<Map<String,String>>(rsFile); list = new FileBackedListRs<List<String>>(rsFileOnly, connectionHttp); } else { // Build the column position map: ColPositionBuilder colPositionBuilder = new ColPositionBuilder(rsFile, connectionHttp); colPosition = colPositionBuilder.getColPosition(); // Build the List of Strings // list = new FileBackedListOfMaps<Map<String,String>>(rsFile); list = new FileBackedListRs<List<String>>(rsFile, connectionHttp); } } /* * (non-Javadoc) * * @see org.kawanfw.sql.jdbc.abstracts.AbstractResultSet#getStatement() */ @Override public Statement getStatement() throws SQLException { return statement; } /** * Get the value of the column using the map inside the List * * @param columnIndex * the first column is 1, the second is 2, ... * @boolean isString if true, the returned value is a String ==> Parse it to * remove special characters ! * @return the value of the column using the map inside the List * * @throws SQLException */ private String getValueOfList(int columnIndex, boolean isString) throws SQLException { if (isBeforeFirst() || isAfterLast()) { throw new SQLException("The ResultSet is not positioned properly, you may need to call next()"); } List<String> theRow = list.get(cursor - 1); if (theRow == null) { String message = Tag.PRODUCT_PRODUCT_FAIL + "No value found in List<String> theRow."; throw new SQLException(message, new IOException(message)); } String value = null; int listIndex = columnIndex - 1; try { value = theRow.get(listIndex); } catch (Exception e) { throw new SQLException( "Column index is out of bounds: " + columnIndex + ". Number of columns: " + theRow.size()); } if (isString) { value = HtmlConverter.fromHtml(value); } wasNull = false; if (value != null && value.equalsIgnoreCase("NULL")) { wasNull = true; } return value; } /** * Get the value of the column using the map inside the List * * @param columnIndex * the first column is 1, the second is 2, ... * @boolean if true, the returned value is a String ==> Parse it to remove * special characters ! * @return the value of the column using the map inside the List * * @throws SQLException */ private byte[] getBinaryValueOfList(int columnIndex) throws SQLException { String value = getValueOfList(columnIndex, false); if (value == null) { // Not sure what to do throw new SQLException("Column Index is out of bound: " + columnIndex); } byte[] bytes = null; // Check if we must get the byte array from an input stream if (value.startsWith(TransportConverter.KAWANFW_BYTES_STREAM_FILE)) { InputStream in = null; String remoteFile = StringUtils.substringAfter(value, TransportConverter.KAWANFW_BYTES_STREAM_FILE); // HACK if (!remoteFile.startsWith("/")) { remoteFile = "/" + remoteFile; } try { in = getInputStreamFromRemoteFile(remoteFile); bytes = getBytesFromInputStream(in); } finally { IOUtils.closeQuietly(in); } } else { bytes = TransportConverter.fromTransportFormatToBytes(value); } return bytes; } /** * Get the value of the column using the map inside the List * * @param columnIndex * the first column is 1, the second is 2, ... * @boolean if true, the returned value is a String ==> Parse it to remove * special characters ! * @return the value of the column using the map inside the List * * @throws SQLException */ private InputStream getInputStreamOfMap(int columnIndex) throws SQLException { String value = getValueOfList(columnIndex, false); if (value == null) { // Not sure what to do throw new SQLException("Column Index is out of bound: " + columnIndex); } InputStream in = null; // Check if we must get the byte array from an input stream if (value.startsWith(TransportConverter.KAWANFW_BYTES_STREAM_FILE)) { String remoteFile = StringUtils.substringAfter(value, TransportConverter.KAWANFW_BYTES_STREAM_FILE); // HACK if (!remoteFile.startsWith("/")) { remoteFile = "/" + remoteFile; } in = getInputStreamFromRemoteFile(remoteFile); } else { byte[] bytes = TransportConverter.fromTransportFormatToBytes(value); in = new ByteArrayInputStream(bytes); } return in; } /** * Return a InputStream from a remote file * * @param remoteFile * the generic name of the remote file * @return the input stream corresponding to the remote file * * @throws SQLException */ private InputStream getInputStreamFromRemoteFile(String remoteFile) throws SQLException { InputStream in = null; // Be cause we open an InputWtream on remot file, we have to // say to delete it at cose() remoteFiles.add(remoteFile); debug("begin - remoteFile: " + remoteFile); // if remote file length is // TransportConverter.KAWANFW_STREAM_NULL.length() // There is huge probability that its' a null stream message, so // download it and test it with a temp stream RemoteSession remoteSession = connectionHttp.getRemoteSession(); try { List<String> remoteFiles = new ArrayList<String>(); remoteFiles.add(remoteFile); long length = remoteSession.length(remoteFiles); if (length == TransportConverter.KAWANFW_STREAM_NULL.length()) { boolean isNull = isInputStreamNull(remoteFile); if (isNull) { RemoteInputStream nullStream = null; return nullStream; } else { // Ok remote file does not correspond to null input stream in = new RemoteInputStream(remoteSession, remoteFile); } } else { // Now use a remote input stream in = new RemoteInputStream(remoteSession, remoteFile); } } catch (Exception e) { JdbcHttpTransferUtil.wrapExceptionAsSQLException(e); } return in; } /** * Tells if the remote says the input stream is null * * @param remoteFile * the remote file * @return * @throws Exception * @throws IOException * @throws FileNotFoundException */ private boolean isInputStreamNull(String remoteFile) throws Exception, IOException, FileNotFoundException { File localFile = BlobHttp.createUniqueBlobFile(); // Simple download RemoteSession remoteSession = connectionHttp.getRemoteSession(); remoteSession.download(remoteFile, localFile); debug("localFile: " + localFile); try { if (localFile.length() == TransportConverter.KAWANFW_STREAM_NULL.length()) { debug("before content"); String content = FileUtils.readFileToString(localFile); debug("content: " + content); // Return null if the file content is // TransportConverter.KAWANFW_STREAM_NULL if (content.contains(TransportConverter.KAWANFW_STREAM_NULL)) { return true; } } debug("localFile: " + localFile); return false; } finally { // We won't reuse the local file so delete it! localFile.delete(); } } /** * Return a Reader from a remote file * * @param remoteFile * the generic name of the remote file * @return the Reader corresponding to the remote file * * @throws SQLException */ private Reader getReaderFromRemoteFile(String remoteFile) throws SQLException { Reader reader = null; try { File localFile = ClobHttp.createUniqueClobFile(); downloadBlobClob(localFile, remoteFile); if (localFile.length() == TransportConverter.KAWANFW_STREAM_NULL.length()) { String content = FileUtils.readFileToString(localFile); // Return null if the file content is // TransportConverter.KAWANFW_STREAM_NULL if (content.contains(TransportConverter.KAWANFW_STREAM_NULL)) { localFiles.add(localFile); return null; } } reader = new BufferedReader(new FileReader(getLocalFileHtmlDecoded(localFile))); } catch (Exception e) { throw new SQLException(e.getMessage(), e); } return reader; } /** * Download a remote file into a local file * * @param localFile * the local file to create a Clob * @param remoteFile * the corresponding remote file * @throws SQLException */ private void downloadBlobClob(File localFile, String remoteFile) throws Exception { RemoteSession remoteSession = connectionHttp.getRemoteSession(); debug("getInputStreamFromRemoteFile: " + remoteFile); List<String> remoteFiles = new ArrayList<String>(); remoteFiles.add(remoteFile); long filesLength = remoteSession.length(remoteFiles); debug(""); debug("getInputStreamFromRemoteFile()"); debug("localFile : " + localFile); debug("remoteFile: " + remoteFile); // reinit progress connectionHttp.getProgress().set(0); FilesTransferWithProgress filesTransferWithProgress = new FilesTransferWithProgress( connectionHttp.getRemoteSession(), connectionHttp.getProgress(), connectionHttp.getCancelled()); // HACK if (!remoteFile.startsWith("/")) { remoteFile = "/" + remoteFile; } filesTransferWithProgress.download(remoteFile, localFile, filesLength); // Fire and forget in thread deleteRemoteBlobFile(remoteFile, remoteSession); } /** * @param remoteFile * @param fileSession */ private void deleteRemoteBlobFile(String remoteFile, RemoteSession remoteSession) { // if (KeepTempFilePolicyParms.KEEP_TEMP_FILE || DEBUG) { // return; // } // final FileSession fileSessionDelete = fileSession.clone(); // final String remoteFileDelete = remoteFile; // // Runnable doWorkRunnable = new Runnable() { // public void run() { // // try { // fileSessionDelete.delete(remoteFileDelete); // } catch (Exception e) { // e.printStackTrace(); // } // } // }; // // doWorkRunnable.run(); try { // fileSession.delete(remoteFile); RemoteFile theRemoteFile = new RemoteFile(remoteSession, remoteFile); theRemoteFile.delete(); } catch (Exception e) { e.printStackTrace(); } } /** * Return the content of an input stream into a byte array * * @param in * the input stream * @return the read byte array * * @throws SQLException */ private byte[] getBytesFromInputStream(InputStream in) throws SQLException { if (in == null) { return null; } ByteArrayOutputStream bos = null; try { bos = new ByteArrayOutputStream(); IOUtils.copy(in, bos); } catch (IOException e) { throw new SQLException(e.getMessage(), e); } finally { IOUtils.closeQuietly(in); } return bos.toByteArray(); } /** * Displays the given message if DEBUG is set. * * @param sMsg * the debug message */ private void debug(String s) { if (DEBUG) { ClientLogger.getLogger().log(Level.WARNING, s); } } /** * Retrieves the number, types and properties of this <code>ResultSet</code> * object's columns. * * @return the description of this <code>ResultSet</code> object's columns * @exception SQLException * if a database access error occurs */ public ResultSetMetaData getMetaData() throws SQLException { ResultSetMetaData resultSetMetaDataLocal = resultSetMetaDataMap.get(this); if (resultSetMetaDataLocal != null) { debug("getMetaData(): return ResultSetMetaData from cache."); return resultSetMetaDataLocal; } File receiveFile = null; if (rsMetaDataFileTypeNormal != null && statementHolder != null) { debug("getMetaData(): receiveFile = rsMetaDataFileTypeNormal. "); receiveFile = rsMetaDataFileTypeNormal; } else if (statementHolder != null) { debug("getMetaData(): contacting server. "); // We modify statementHolder to say that it's for a getMetaData() // request statementHolder.setDoExtractResultSetMetaData(true); // We must *decrypt* it before passing it to // JdbcHttpStatementTransfer! // This is because it's a call back... SessionParameters sessionParameters = connectionHttp.getSessionParameters(); if (sessionParameters != null && sessionParameters.getEncryptionPassword() != null) { char[] password = sessionParameters.getEncryptionPassword(); StatementHolderCrypto statementHolderCrypto = new StatementHolderCrypto(statementHolder, password); try { statementHolder = statementHolderCrypto.decrypt(); } catch (Exception e) { throw new SQLException(e); } } JdbcHttpStatementTransfer jdbcHttpStatementTransfer = new JdbcHttpStatementTransfer(connectionHttp, connectionHttp.getAuthenticationToken()); receiveFile = jdbcHttpStatementTransfer.getFileFromExecuteQueryOnServer(statementHolder); debug("getFileFromexecuteOnServer() : " + receiveFile); } else if (rsMetaDataFileTypeFunction != null) { receiveFile = rsMetaDataFileTypeFunction; } else { throw new SQLException("statementHolder and rsMetaDataFile can not be both null!"); } debug("getMetaData.statementHolder: " + statementHolder); // The file contains one line of JSON String jsonString = null; try { jsonString = FileUtils.readFileToString(receiveFile); debug("getMetaData().jsonString: " + jsonString); debug("jsonString.length() : " + jsonString.length()); jsonString = jsonString.trim(); // Very important when encrypted // string // It may be encrypted jsonString = JsonLineDecryptor.decrypt(jsonString, connectionHttp); debug("jsonString.decrypted() : " + jsonString); } catch (IOException e) { throw new SQLException(e.getMessage(), e); } localFiles.add(receiveFile); jsonString = HtmlConverter.fromHtml(jsonString); ResultSetMetaDataHolder resultSetMetaDataHolder = ResultSetMetaDataHolderTransport.fromJson(jsonString); ResultSetMetaData resultSetMetaData = new ResultSetMetaDataHttp(resultSetMetaDataHolder); resultSetMetaDataMap.put(this, resultSetMetaData); return resultSetMetaData; } /** * Retrieves the first warning reported by calls on this * <code>ResultSet</code> object. Subsequent warnings on this * <code>ResultSet</code> object will be chained to the * <code>SQLWarning</code> object that this method returns. * * <P> * The warning chain is automatically cleared each time a new row is read. * This method may not be called on a <code>ResultSet</code> object that has * been closed; doing so will cause an <code>SQLException</code> to be * thrown. * <P> * <B>Note:</B> This warning chain only covers warnings caused by * <code>ResultSet</code> methods. Any warning caused by * <code>Statement</code> methods (such as reading OUT parameters) will be * chained on the <code>Statement</code> object. * * @return the first <code>SQLWarning</code> object reported or * <code>null</code> if there are none * @exception SQLException * if a database access error occurs or this method is called * on a closed result set */ @Override public SQLWarning getWarnings() throws SQLException { return null; } /** * Clears all warnings reported on this <code>ResultSet</code> object. After * this method is called, the method <code>getWarnings</code> returns * <code>null</code> until a new warning is reported for this * <code>ResultSet</code> object. * * @exception SQLException * if a database access error occurs */ @Override public void clearWarnings() throws SQLException { // does nothing } /** * Moves the cursor down one row from its current position. A * <code>ResultSet</code> cursor is initially positioned before the first * row; the first call to the method <code>next</code> makes the first row * the current row; the second call makes the second row the current row, * and so on. * * <P> * If an input stream is open for the current row, a call to the method * <code>next</code> will implicitly close it. A <code>ResultSet</code> * object's warning chain is cleared when a new row is read. * * @return <code>true</code> if the new current row is valid; * <code>false</code> if there are no more rows * @exception SQLException * if a database access error occurs */ @Override public boolean next() throws SQLException { testIfClosed(); if (list == null) { String message = Tag.PRODUCT_PRODUCT_FAIL + "The List of Maps (Column Name, Column Value) is null!"; throw new SQLException(message, new IOException(message)); } cursor++; if (cursor > list.size()) { return false; } else { return true; } } // --------------------------------------------------------------------- // Traversal/Positioning // --------------------------------------------------------------------- /** * Retrieves whether the cursor is before the first row in this * <code>ResultSet</code> object. * * @return <code>true</code> if the cursor is before the first row; * <code>false</code> if the cursor is at any other position or the * result set contains no rows * @exception SQLException * if a database access error occurs * @since 1.2 */ @Override public boolean isBeforeFirst() throws SQLException { testIfClosed(); if (cursor == 0) { return true; } else { return false; } } /** * Retrieves whether the cursor is after the last row in this * <code>ResultSet</code> object. * * @return <code>true</code> if the cursor is after the last row; * <code>false</code> if the cursor is at any other position or the * result set contains no rows * @exception SQLException * if a database access error occurs * @since 1.2 */ @Override public boolean isAfterLast() throws SQLException { testIfClosed(); if (cursor >= list.size() + 1) { return true; } else { return false; } } /** * Retrieves whether the cursor is on the first row of this * <code>ResultSet</code> object. * * @return <code>true</code> if the cursor is on the first row; * <code>false</code> otherwise * @exception SQLException * if a database access error occurs * @since 1.2 */ @Override public boolean isFirst() throws SQLException { testIfClosed(); if (cursor == 1) { return true; } else { return false; } } /** * Retrieves whether the cursor is on the last row of this * <code>ResultSet</code> object. Note: Calling the method * <code>isLast</code> may be expensive because the JDBC driver might need * to fetch ahead one row in order to determine whether the current row is * the last row in the result set. * * @return <code>true</code> if the cursor is on the last row; * <code>false</code> otherwise * @exception SQLException * if a database access error occurs * @since 1.2 */ @Override public boolean isLast() throws SQLException { testIfClosed(); if (cursor == list.size()) { return true; } else { return false; } } /** * Moves the cursor to the front of this <code>ResultSet</code> object, just * before the first row. This method has no effect if the result set * contains no rows. * * @exception SQLException * if a database access error occurs or the result set type * is <code>TYPE_FORWARD_ONLY</code> * @since 1.2 */ @Override public void beforeFirst() throws SQLException { testIfClosed(); cursor = 0; } /** * Moves the cursor to the end of this <code>ResultSet</code> object, just * after the last row. This method has no effect if the result set contains * no rows. * * @exception SQLException * if a database access error occurs or the result set type * is <code>TYPE_FORWARD_ONLY</code> * @since 1.2 */ @Override public void afterLast() throws SQLException { testIfClosed(); cursor = list.size() + 1; } /** * Moves the cursor to the first row in this <code>ResultSet</code> object. * * @return <code>true</code> if the cursor is on a valid row; * <code>false</code> if there are no rows in the result set * @exception SQLException * if a database access error occurs or the result set type * is <code>TYPE_FORWARD_ONLY</code> * @since 1.2 */ @Override public boolean first() throws SQLException { testIfClosed(); cursor = 1; return true; } /** * Moves the cursor to the last row in this <code>ResultSet</code> object. * * @return <code>true</code> if the cursor is on a valid row; * <code>false</code> if there are no rows in the result set * @exception SQLException * if a database access error occurs or the result set type * is <code>TYPE_FORWARD_ONLY</code> * @since 1.2 */ @Override public boolean last() throws SQLException { testIfClosed(); cursor = list.size(); return true; } /** * Retrieves the current row number. The first row is number 1, the second * number 2, and so on. * * @return the current row number; <code>0</code> if there is no current row * @exception SQLException * if a database access error occurs * @since 1.2 */ @Override public int getRow() throws SQLException { testIfClosed(); if (isAfterLast()) { return 0; } return cursor; } /** * Moves the cursor to the given row number in this <code>ResultSet</code> * object. * * <p> * If the row number is positive, the cursor moves to the given row number * with respect to the beginning of the result set. The first row is row 1, * the second is row 2, and so on. * * <p> * If the given row number is negative, the cursor moves to an absolute row * position with respect to the end of the result set. For example, calling * the method <code>absolute(-1)</code> positions the cursor on the last * row; calling the method <code>absolute(-2)</code> moves the cursor to the * next-to-last row, and so on. * * <p> * An attempt to position the cursor beyond the first/last row in the result * set leaves the cursor before the first row or after the last row. * * <p> * <B>Note:</B> Calling <code>absolute(1)</code> is the same as calling * <code>first()</code>. Calling <code>absolute(-1)</code> is the same as * calling <code>last()</code>. * * @param row * the number of the row to which the cursor should move. A * positive number indicates the row number counting from the * beginning of the result set; a negative number indicates the * row number counting from the end of the result set * @return <code>true</code> if the cursor is on the result set; * <code>false</code> otherwise * @exception SQLException * if a database access error occurs, or the result set type * is <code>TYPE_FORWARD_ONLY</code> * @since 1.2 */ @Override public boolean absolute(int row) throws SQLException { testIfClosed(); cursor = row; if (cursor < 0) { cursor = 0; } if (isAfterLast() || isBeforeFirst()) { return false; } else { return true; } } /** * Moves the cursor a relative number of rows, either positive or negative. * Attempting to move beyond the first/last row in the result set positions * the cursor before/after the the first/last row. Calling * <code>relative(0)</code> is valid, but does not change the cursor * position. * * <p> * Note: Calling the method <code>relative(1)</code> is identical to calling * the method <code>next()</code> and calling the method * <code>relative(-1)</code> is identical to calling the method * <code>previous()</code>. * * @param rows * an <code>int</code> specifying the number of rows to move from * the current row; a positive number moves the cursor forward; a * negative number moves the cursor backward * @return <code>true</code> if the cursor is on a row; <code>false</code> * otherwise * @exception SQLException * if a database access error occurs, there is no current * row, or the result set type is * <code>TYPE_FORWARD_ONLY</code> * @since 1.2 */ @Override public boolean relative(int rows) throws SQLException { testIfClosed(); cursor += rows; if (cursor < 0) { cursor = 0; } if (isAfterLast() || isBeforeFirst()) { return false; } else { return true; } } /** * Moves the cursor to the previous row in this <code>ResultSet</code> * object. * * @return <code>true</code> if the cursor is on a valid row; * <code>false</code> if it is off the result set * @exception SQLException * if a database access error occurs or the result set type * is <code>TYPE_FORWARD_ONLY</code> * @since 1.2 */ @Override public boolean previous() throws SQLException { testIfClosed(); cursor--; if (cursor < 1) { return false; } else { return true; } } // ====================================================================== // Methods for accessing results by column index // ====================================================================== /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>String</code> in the Java * programming language. * * @param columnIndex * the first column is 1, the second is 2, ... * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>null</code> * @exception SQLException * if a database access error occurs */ @Override public String getString(int columnIndex) throws SQLException { testIfClosed(); String s = getValueOfList(columnIndex, true); if (s == null || s.equalsIgnoreCase("NULL")) { return null; } // Test if have a Clob if (s.startsWith(TransportConverter.KAWANFW_BYTES_STREAM_FILE)) { return getFileAsString(s); } if (s.startsWith(UrlTransporter.URL_HEADER)) { // It's an URL ==> Transform it to String UrlTransporter urlTransporter = new UrlTransporter(); URL url = null; try { url = urlTransporter.fromBase64(s); return url.toString(); } catch (Exception e) { throw new SQLException(e); } } return s; } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>boolean</code> in the Java * programming language. * * @param columnIndex * the first column is 1, the second is 2, ... * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>false</code> * @exception SQLException * if a database access error occurs */ @Override public boolean getBoolean(int columnIndex) throws SQLException { testIfClosed(); String s = getValueOfList(columnIndex, false); if (s == null || s.equalsIgnoreCase("NULL")) { return false; } // For Oracle: "1", for SQL Anywhere: 1,00000 if (s.startsWith("1")) { return true; } try { return Boolean.parseBoolean(s); } catch (Exception e) { String methodName = new Object() { }.getClass().getEnclosingMethod().getName(); throw new SQLException("Invalid value for " + methodName + "(): " + "\'" + s + "\'"); } } /** * /** Retrieves the value of the designated column in the current row of * this <code>ResultSet</code> object as a <code>byte</code> in the Java * programming language. * * @param columnIndex * the first column is 1, the second is 2, ... * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>0</code> * @exception SQLException * if a database access error occurs */ @Override public byte getByte(int columnIndex) throws SQLException { testIfClosed(); byte[] bytes = getBinaryValueOfList(columnIndex); if (bytes == null) { wasNull = true; return -1; } byte theByte = bytes[0]; return theByte; } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>short</code> in the Java * programming language. * * @param columnIndex * the first column is 1, the second is 2, ... * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>0</code> * @exception SQLException * if a database access error occurs */ @Override public short getShort(int columnIndex) throws SQLException { testIfClosed(); String s = getValueOfList(columnIndex, false); if (s == null || s.equalsIgnoreCase("NULL")) { return -1; } try { return Short.parseShort(s); } catch (Exception e) { String methodName = new Object() { }.getClass().getEnclosingMethod().getName(); throw new SQLException("Invalid value for " + methodName + "(): " + "\'" + s + "\'"); } } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as an <code>int</code> in the Java * programming language. * * @param columnIndex * the first column is 1, the second is 2, ... * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>0</code> * @exception SQLException * if a database access error occurs */ @Override public int getInt(int columnIndex) throws SQLException { testIfClosed(); String s = getValueOfList(columnIndex, false); if (s == null || s.equalsIgnoreCase("NULL")) { return -1; } try { return Integer.parseInt(s); } catch (Exception e) { String methodName = new Object() { }.getClass().getEnclosingMethod().getName(); throw new SQLException("Invalid value for " + methodName + "(): " + "\'" + s + "\'"); } } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>long</code> in the Java * programming language. * * @param columnIndex * the first column is 1, the second is 2, ... * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>0</code> * @exception SQLException * if a database access error occurs */ @Override public long getLong(int columnIndex) throws SQLException { testIfClosed(); String s = getValueOfList(columnIndex, false); if (s == null || s.equalsIgnoreCase("NULL")) { return -1; } try { return Long.parseLong(s); } catch (Exception e) { String methodName = new Object() { }.getClass().getEnclosingMethod().getName(); throw new SQLException("Invalid value for " + methodName + "(): " + "\'" + s + "\'"); } } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>float</code> in the Java * programming language. * * @param columnIndex * the first column is 1, the second is 2, ... * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>0</code> * @exception SQLException * if a database access error occurs */ @Override public float getFloat(int columnIndex) throws SQLException { testIfClosed(); String s = getValueOfList(columnIndex, false); if (s == null || s.equalsIgnoreCase("NULL")) { return -1; } try { return Float.parseFloat(s); } catch (Exception e) { String methodName = new Object() { }.getClass().getEnclosingMethod().getName(); throw new SQLException("Invalid value for " + methodName + "(): " + "\'" + s + "\'"); } } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>double</code> in the Java * programming language. * * @param columnIndex * the first column is 1, the second is 2, ... * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>0</code> * @exception SQLException * if a database access error occurs */ @Override public double getDouble(int columnIndex) throws SQLException { testIfClosed(); String s = getValueOfList(columnIndex, false); if (s == null || s.equalsIgnoreCase("NULL")) { return -1; } try { return Double.parseDouble(s); } catch (Exception e) { String methodName = new Object() { }.getClass().getEnclosingMethod().getName(); throw new SQLException("Invalid value for " + methodName + "(): " + "\'" + s + "\'"); } } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>java.math.BigDecimal</code> with * full precision. * * @param columnIndex * the first column is 1, the second is 2, ... * @return the column value (full precision); if the value is SQL * <code>NULL</code>, the value returned is <code>null</code> in the * Java programming language. * @exception SQLException * if the columnIndex is not valid; if a database access * error occurs or this method is called on a closed result * set * @since 1.2 */ public BigDecimal getBigDecimal(int columnIndex) throws SQLException { testIfClosed(); String s = getValueOfList(columnIndex, false); if (s == null || s.equalsIgnoreCase("NULL")) { return null; } try { return new BigDecimal(s); } catch (Exception e) { String methodName = new Object() { }.getClass().getEnclosingMethod().getName(); throw new SQLException("Invalid value for " + methodName + "(): " + "\'" + s + "\'"); } } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>java.sql.BigDecimal</code> in * the Java programming language. * * @param columnIndex * the first column is 1, the second is 2, ... * @param scale * the number of digits to the right of the decimal point * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>null</code> * @exception SQLException * if a database access error occurs * @deprecated */ @Override public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException { if (scale < 0) { throw new SQLException("invalid scale: " + scale + ". Scale must be >= 0"); } BigDecimal bd = getBigDecimal(columnIndex); if (bd != null) { bd = bd.setScale(scale, BigDecimal.ROUND_DOWN); } return bd; } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>byte</code> array in the Java * programming language. The bytes represent the raw values returned by the * driver. * * @param columnIndex * the first column is 1, the second is 2, ... * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>null</code> * @exception SQLException * if a database access error occurs */ @Override public byte[] getBytes(int columnIndex) throws SQLException { testIfClosed(); byte[] bytes = getBinaryValueOfList(columnIndex); if (bytes == null) { wasNull = true; } return bytes; } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>java.sql.Date</code> object in * the Java programming language. * * @param columnIndex * the first column is 1, the second is 2, ... * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>null</code> * @exception SQLException * if a database access error occurs */ @Override public java.sql.Date getDate(int columnIndex) throws SQLException { testIfClosed(); String s = getValueOfList(columnIndex, false); if (s == null || s.equalsIgnoreCase("NULL")) { return null; } try { return timestampOrDateValueOf(s); } catch (Exception e) { String methodName = new Object() { }.getClass().getEnclosingMethod().getName(); throw new SQLException("Invalid value for " + methodName + "(): " + "\'" + s + "\'"); } } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>java.sql.Time</code> object in * the Java programming language. * * @param columnIndex * the first column is 1, the second is 2, ... * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>null</code> * @exception SQLException * if a database access error occurs */ @Override public java.sql.Time getTime(int columnIndex) throws SQLException { testIfClosed(); String s = getValueOfList(columnIndex, false); if (s == null || s.equalsIgnoreCase("NULL")) { return null; } try { return java.sql.Time.valueOf(s); } catch (Exception e) { String methodName = new Object() { }.getClass().getEnclosingMethod().getName(); throw new SQLException("Invalid value for " + methodName + "(): " + "\'" + s + "\'"); } } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>java.sql.Timestamp</code> object * in the Java programming language. * * @param columnIndex * the first column is 1, the second is 2, ... * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>null</code> * @exception SQLException * if a database access error occurs */ @Override public java.sql.Timestamp getTimestamp(int columnIndex) throws SQLException { testIfClosed(); String s = getValueOfList(columnIndex, false); if (s == null || s.equalsIgnoreCase("NULL")) { return null; } try { return java.sql.Timestamp.valueOf(s); } catch (Exception e) { String methodName = new Object() { }.getClass().getEnclosingMethod().getName(); throw new SQLException("Invalid value for " + methodName + "(): " + "\'" + s + "\'"); } } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>java.net.URL</code> object in * the Java programming language. * * @param columnIndex * the index of the column 1 is the first, 2 is the second,... * @return the column value as a <code>java.net.URL</code> object; if the * value is SQL <code>NULL</code>, the value returned is * <code>null</code> in the Java programming language * @exception SQLException * if a database access error occurs, or if a URL is * malformed * @since 1.4 */ @Override public URL getURL(int columnIndex) throws SQLException { testIfClosed(); String s = getValueOfList(columnIndex, false); if (s == null || s.equalsIgnoreCase("NULL")) { return null; } debug("getUrl(columnIndex) : " + s); UrlTransporter urlTransporter = new UrlTransporter(); URL url = null; try { url = urlTransporter.fromBase64(s); return url; } catch (ClassNotFoundException e) { String reason = Tag.PRODUCT_EXCEPTION_RAISED + " Impossible to convert BASE64 serialized URL back to URL"; throw new SQLException(reason, e); } catch (Exception e) { String methodName = new Object() { }.getClass().getEnclosingMethod().getName(); throw new SQLException("Invalid value for " + methodName + "(): " + "\'" + s + "\'"); } } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as an <code>Array</code> object in the Java * programming language. * * @param i * the first column is 1, the second is 2, ... * @return an <code>Array</code> object representing the SQL * <code>ARRAY</code> value in the specified column * @exception SQLException * if a database access error occurs * @since 1.2 */ @Override public Array getArray(int i) throws SQLException { testIfClosed(); String s = getValueOfList(i, false); if (s == null || s.equalsIgnoreCase("NULL")) { return null; } debug("getArray(i) : " + s); ArrayTransporter arrayTransporter = new ArrayTransporter(); Array array = null; try { array = arrayTransporter.fromBase64(s); debug("array : " + array.toString()); return array; } catch (ClassNotFoundException e) { String reason = Tag.PRODUCT_EXCEPTION_RAISED + " Impossible to convert BASE64 serialized Array back to Array"; throw new SQLException(reason, e); } catch (Exception e) { String methodName = new Object() { }.getClass().getEnclosingMethod().getName(); throw new SQLException("Invalid value for " + methodName + "(): " + "\'" + s + "\'"); } } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>String</code> in the Java * programming language. It is intended for use when accessing * <code>NCHAR</code>,<code>NVARCHAR</code> and <code>LONGNVARCHAR</code> * columns. * * @param columnIndex * the first column is 1, the second is 2, ... * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>null</code> * @exception SQLException * if the columnIndex is not valid; if a database access * error occurs or this method is called on a closed result * set * @exception SQLFeatureNotSupportedException * if the JDBC driver does not support this method * @since 1.6 */ @Override public String getNString(int columnIndex) throws SQLException { // Translation was already done on server... return getString(columnIndex); } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>java.sql.RowId</code> object in * the Java programming language. * * @param columnIndex * the first column is 1, the second 2, ... * @return the column value; if the value is a SQL <code>NULL</code> the * value returned is <code>null</code> * @throws SQLException * if the columnIndex is not valid; if a database access error * occurs or this method is called on a closed result set * @exception SQLFeatureNotSupportedException * if the JDBC driver does not support this method * @since 1.6 */ public RowId getRowId(int columnIndex) throws SQLException { testIfClosed(); String s = getValueOfList(columnIndex, false); if (s == null || s.equalsIgnoreCase("NULL")) { return null; } debug("getRowId(columnLabel) : " + s); RowIdTransporter rowIdTransporter = new RowIdTransporter(); RowId rowId = null; try { rowId = rowIdTransporter.fromBase64(s); return rowId; } catch (ClassNotFoundException e) { String reason = Tag.PRODUCT_EXCEPTION_RAISED + " Impossible to convert BASE64 serialized RowId back to RowId"; throw new SQLException(reason, e); } catch (Exception e) { String methodName = new Object() { }.getClass().getEnclosingMethod().getName(); throw new SQLException("Invalid value for " + methodName + "(): " + "\'" + s + "\'"); } } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>String</code> in the Java * programming language. * * @param columnName * the SQL name of the column * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>null</code> * @exception SQLException * if a database access error occurs */ @Override public String getString(String columnName) throws SQLException { int columnIndex = findColumn(columnName); return getString(columnIndex); } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>String</code> in the Java * programming language. It is intended for use when accessing * <code>NCHAR</code>,<code>NVARCHAR</code> and <code>LONGNVARCHAR</code> * columns. * * @param columnLabel * the label for the column specified with the SQL AS clause. If * the SQL AS clause was not specified, then the label is the * name of the column * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>null</code> * @exception SQLException * if the columnLabel is not valid; if a database access * error occurs or this method is called on a closed result * set * @exception SQLFeatureNotSupportedException * if the JDBC driver does not support this method * @since 1.6 */ @Override public String getNString(String columnLabel) throws SQLException { int columnIndex = findColumn(columnLabel); return getNString(columnIndex); } /* * (non-Javadoc) * * @see * org.kawanfw.commons.jdbc.abstracts.AbstractResultSet#getURL(java.lang * .String) */ @Override public URL getURL(String columnName) throws SQLException { int columnIndex = findColumn(columnName); return getURL(columnIndex); } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as an <code>Array</code> object in the Java * programming language. * * @param colName * the name of the column from which to retrieve the value * @return an <code>Array</code> object representing the SQL * <code>ARRAY</code> value in the specified column * @exception SQLException * if a database access error occurs * @since 1.2 */ @Override public Array getArray(String colName) throws SQLException { int columnIndex = findColumn(colName); return getArray(columnIndex); } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>java.sql.RowId</code> object in * the Java programming language. * * @param columnLabel * the label for the column specified with the SQL AS clause. If * the SQL AS clause was not specified, then the label is the * name of the column * @return the column value ; if the value is a SQL <code>NULL</code> the * value returned is <code>null</code> * @throws SQLException * if the columnLabel is not valid; if a database access error * occurs or this method is called on a closed result set * @exception SQLFeatureNotSupportedException * if the JDBC driver does not support this method * @since 1.6 */ public RowId getRowId(String columnLabel) throws SQLException { int columnIndex = findColumn(columnLabel); return getRowId(columnIndex); } /** * Return the underlying file as a string * * @param s * the file address & name * @return the content as string * @throws SQLException */ private String getFileAsString(String s) throws SQLException { Reader reader = null; String remoteFile = StringUtils.substringAfter(s, TransportConverter.KAWANFW_BYTES_STREAM_FILE); try { reader = getReaderFromRemoteFile(remoteFile); if (reader == null) { return null; } StringWriter stringWriter = new StringWriter(); IOUtils.copy(reader, stringWriter); return stringWriter.toString(); } catch (Exception e) { throw new SQLException(e.getMessage(), e); } finally { IOUtils.closeQuietly(reader); } } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>boolean</code> in the Java * programming language. * * @param columnName * the SQL name of the column * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>false</code> * @exception SQLException * if a database access error occurs */ @Override public boolean getBoolean(String columnName) throws SQLException { int columnIndex = findColumn(columnName); return getBoolean(columnIndex); } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>byte</code> in the Java * programming language. * * @param columnName * the SQL name of the column * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>0</code> * @exception SQLException * if a database access error occurs */ @Override public byte getByte(String columnName) throws SQLException { int columnIndex = findColumn(columnName); return getByte(columnIndex); } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>short</code> in the Java * programming language. * * @param columnName * the SQL name of the column * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>0</code> * @exception SQLException * if a database access error occurs */ @Override public short getShort(String columnName) throws SQLException { int columnIndex = findColumn(columnName); return getShort(columnIndex); } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as an <code>int</code> in the Java * programming language. * * @param columnName * the SQL name of the column * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>0</code> * @exception SQLException * if a database access error occurs */ @Override public int getInt(String columnName) throws SQLException { int columnIndex = findColumn(columnName); return getInt(columnIndex); } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>long</code> in the Java * programming language. * * @param columnName * the SQL name of the column * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>0</code> * @exception SQLException * if a database access error occurs */ @Override public long getLong(String columnName) throws SQLException { int columnIndex = findColumn(columnName); return getLong(columnIndex); } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>float</code> in the Java * programming language. * * @param columnName * the SQL name of the column * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>0</code> * @exception SQLException * if a database access error occurs */ @Override public float getFloat(String columnName) throws SQLException { int columnIndex = findColumn(columnName); return getFloat(columnIndex); } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>double</code> in the Java * programming language. * * @param columnName * the SQL name of the column * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>0</code> * @exception SQLException * if a database access error occurs */ @Override public double getDouble(String columnName) throws SQLException { int columnIndex = findColumn(columnName); return getDouble(columnIndex); } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>java.math.BigDecimal</code> with * full precision. * * @param columnLabel * the label for the column specified with the SQL AS clause. If * the SQL AS clause was not specified, then the label is the * name of the column * @return the column value (full precision); if the value is SQL * <code>NULL</code>, the value returned is <code>null</code> in the * Java programming language. * @exception SQLException * if the columnLabel is not valid; if a database access * error occurs or this method is called on a closed result * set * @since 1.2 * */ @Override public BigDecimal getBigDecimal(String columnLabel) throws SQLException { int columnIndex = findColumn(columnLabel); return getBigDecimal(columnIndex); } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>java.math.BigDecimal</code> in * the Java programming language. * * @param columnName * the SQL name of the column * @param scale * the number of digits to the right of the decimal point * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>null</code> * @exception SQLException * if a database access error occurs * @deprecated */ @Override public BigDecimal getBigDecimal(String columnName, int scale) throws SQLException { int columnIndex = findColumn(columnName); return getBigDecimal(columnIndex, scale); } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>byte</code> array in the Java * programming language. The bytes represent the raw values returned by the * driver. * * @param columnName * the SQL name of the column * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>null</code> * @exception SQLException * if a database access error occurs */ @Override public byte[] getBytes(String columnName) throws SQLException { int columnIndex = findColumn(columnName); return getBytes(columnIndex); } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>java.sql.Date</code> object in * the Java programming language. * * @param columnName * the SQL name of the column * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>null</code> * @exception SQLException * if a database access error occurs */ @Override public java.sql.Date getDate(String columnName) throws SQLException { int columnIndex = findColumn(columnName); return getDate(columnIndex); } /** * Because ORACLE always format Date as Timestamp, we must test the length * of the string return a java.sql.Date formated from a Timestamp.toString() * or java.sql.Date.toString() input * * @param s * the Timestamp.toString() or java.sql.Date.toString() input * @return the java.sql.Date corresponding to the Timestamp.toString() or * java.sql.Date.toString() input */ private java.sql.Date timestampOrDateValueOf(String s) { // Because ORACLE always format Date as Timestamp, we must test the // length of the string if (s.length() > "yyyy-mm-dd".length()) { Timestamp ts = Timestamp.valueOf(s); java.sql.Date date = new java.sql.Date(ts.getTime()); return date; } else { return java.sql.Date.valueOf(s); } } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>java.sql.Time</code> object in * the Java programming language. * * @param columnName * the SQL name of the column * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>null</code> * @exception SQLException * if a database access error occurs */ @Override public java.sql.Time getTime(String columnName) throws SQLException { int columnIndex = findColumn(columnName); return getTime(columnIndex); } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>java.sql.Timestamp</code> * object. * * @param columnName * the SQL name of the column * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>null</code> * @exception SQLException * if a database access error occurs */ @Override public java.sql.Timestamp getTimestamp(String columnName) throws SQLException { int columnIndex = findColumn(columnName); return getTimestamp(columnIndex); } /** * <p> * Gets the value of the designated column in the current row of this * <code>ResultSet</code> object as an <code>Object</code> in the Java * programming language. * * <p> * This method will return the value of the given column as a Java object. * The type of the Java object will be the default Java object type * corresponding to the column's SQL type, following the mapping for * built-in types specified in the JDBC specification. If the value is an * SQL <code>NULL</code>, the driver returns a Java <code>null</code>. * * <p> * This method may also be used to read database-specific abstract data * types. * * In the JDBC 2.0 API, the behavior of method <code>getObject</code> is * extended to materialize data of SQL user-defined types. * <p> * If <code>Connection.getTypeMap</code> does not throw a * <code>SQLFeatureNotSupportedException</code>, then when a column contains * a structured or distinct value, the behavior of this method is as if it * were a call to: <code>getObject(columnIndex, * this.getStatement().getConnection().getTypeMap())</code>. * * If <code>Connection.getTypeMap</code> does throw a * <code>SQLFeatureNotSupportedException</code>, then structured values are * not supported, and distinct values are mapped to the default Java class * as determined by the underlying SQL type of the DISTINCT type. * * @param columnIndex * the first column is 1, the second is 2, ... * @return a <code>java.lang.Object</code> holding the column value * @exception SQLException * if the columnIndex is not valid; if a database access * error occurs or this method is called on a closed result * set */ @Override public Object getObject(int columnIndex) throws SQLException { testIfClosed(); String s = getValueOfList(columnIndex, true); if (s == null || s.equalsIgnoreCase("NULL")) { return null; } return s; } /** * <p> * Gets the value of the designated column in the current row of this * <code>ResultSet</code> object as an <code>Object</code> in the Java * programming language. * * <p> * This method will return the value of the given column as a Java object. * The type of the Java object will be the default Java object type * corresponding to the column's SQL type, following the mapping for * built-in types specified in the JDBC specification. If the value is an * SQL <code>NULL</code>, the driver returns a Java <code>null</code>. * <P> * This method may also be used to read database-specific abstract data * types. * <P> * In the JDBC 2.0 API, the behavior of the method <code>getObject</code> is * extended to materialize data of SQL user-defined types. When a column * contains a structured or distinct value, the behavior of this method is * as if it were a call to: <code>getObject(columnIndex, * this.getStatement().getConnection().getTypeMap())</code>. * * @param columnLabel * the label for the column specified with the SQL AS clause. If * the SQL AS clause was not specified, then the label is the * name of the column * @return a <code>java.lang.Object</code> holding the column value * @exception SQLException * if the columnLabel is not valid; if a database access * error occurs or this method is called on a closed result * set */ @Override public Object getObject(String columnLabel) throws SQLException { int columnIndex = findColumn(columnLabel); return getObject(columnIndex); } // ---------------------------------------------------------------- /** * Maps the given <code>ResultSet</code> column name to its * <code>ResultSet</code> column index. * * @param columnName * the name of the column * @return the column index of the given column name * @exception SQLException * if the <code>ResultSet</code> object does not contain * <code>columnName</code> or a database access error occurs */ public int findColumn(String columnName) throws SQLException { testIfClosed(); if (columnName == null) { throw new NullPointerException("columnName is null!"); } // return getColumnIndexFromColumnName(columnName); columnName = columnName.toLowerCase(); columnName = columnName.trim(); Integer theColPosition = colPosition.get(columnName); if (theColPosition == null) { throw new SQLException("Column Name not found: " + columnName); } // Add to colPosition because result set getters start at 1 theColPosition++; return theColPosition.intValue(); } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>java.io.Reader</code> object. * * @return a <code>java.io.Reader</code> object that contains the column * value; if the value is SQL <code>NULL</code>, the value returned * is <code>null</code> in the Java programming language. * @param columnIndex * the first column is 1, the second is 2, ... * @exception SQLException * if the columnIndex is not valid; if a database access * error occurs or this method is called on a closed result * set * @since 1.2 */ @Override public java.io.Reader getCharacterStream(int columnIndex) throws SQLException { testIfClosed(); String value = getValueOfList(columnIndex, true); if (value == null) { // Not sure what to do throw new SQLException("Column Index is out of bound: " + columnIndex); } Reader reader = null; // Check if we must get the byte array from an input stream if (value.startsWith(TransportConverter.KAWANFW_BYTES_STREAM_FILE)) { String remoteFile = StringUtils.substringAfter(value, TransportConverter.KAWANFW_BYTES_STREAM_FILE); reader = getReaderFromRemoteFile(remoteFile); } else { String stringValue = getString(columnIndex); debug("Reader in String!"); if (stringValue == null) { return null; } else { try { // Put back clean CR_LF BufferedReader bufferedReader = new BufferedReader(new StringReader(stringValue)); StringWriter stringWriter = new StringWriter(); String line = null; while ((line = bufferedReader.readLine()) != null) { stringWriter.write(line + CR_LF); } String cleaned = stringWriter.toString(); reader = new StringReader(cleaned); } catch (Exception e) { throw new SQLException(e.getMessage(), e); } } } return reader; } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>java.io.Reader</code> object. * * @param columnLabel * the label for the column specified with the SQL AS clause. If * the SQL AS clause was not specified, then the label is the * name of the column * @return a <code>java.io.Reader</code> object that contains the column * value; if the value is SQL <code>NULL</code>, the value returned * is <code>null</code> in the Java programming language * @exception SQLException * if the columnLabel is not valid; if a database access * error occurs or this method is called on a closed result * set * @since 1.2 */ @Override public java.io.Reader getCharacterStream(String columnLabel) throws SQLException { int columnIndex = findColumn(columnLabel); return getCharacterStream(columnIndex); } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a stream of ASCII characters. The value * can then be read in chunks from the stream. This method is particularly * suitable for retrieving large <code>LONGVARCHAR</code> values. The JDBC * driver will do any necessary conversion from the database format into * ASCII. * * <P> * <B>Note:</B> All the data in the returned stream must be read prior to * getting the value of any other column. The next call to a getter method * implicitly closes the stream. Also, a stream may return <code>0</code> * when the method <code>available</code> is called whether there is data * available or not. * * @param columnName * the SQL name of the column * @return a Java input stream that delivers the database column value as a * stream of one-byte ASCII characters. If the value is SQL * <code>NULL</code>, the value returned is <code>null</code>. * @exception SQLException * if a database access error occurs */ public java.io.InputStream getAsciiStream(String columnName) throws SQLException { testIfClosed(); int columnIndex = findColumn(columnName); return getAsciiStream(columnIndex); } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a stream of ASCII characters. The value * can then be read in chunks from the stream. This method is particularly * suitable for retrieving large <char>LONGVARCHAR</char> values. The JDBC * driver will do any necessary conversion from the database format into * ASCII. * * <P> * <B>Note:</B> All the data in the returned stream must be read prior to * getting the value of any other column. The next call to a getter method * implicitly closes the stream. Also, a stream may return <code>0</code> * when the method <code>InputStream.available</code> is called whether * there is data available or not. * * @param columnIndex * the first column is 1, the second is 2, ... * @return a Java input stream that delivers the database column value as a * stream of one-byte ASCII characters; if the value is SQL * <code>NULL</code>, the value returned is <code>null</code> * @exception SQLException * if a database access error occurs */ public java.io.InputStream getAsciiStream(int columnIndex) throws SQLException { testIfClosed(); String value = getValueOfList(columnIndex, true); if (value == null) { // Not sure what to do throw new SQLException("Column Index is out of bound: " + columnIndex); } InputStream in = null; // Check if we must get the byte array from an input stream if (value.startsWith(TransportConverter.KAWANFW_BYTES_STREAM_FILE)) { String remoteFile = StringUtils.substringAfter(value, TransportConverter.KAWANFW_BYTES_STREAM_FILE); // HACK if (!remoteFile.startsWith("/")) { remoteFile = "/" + remoteFile; } in = getAsciiInputStreamFromRemoteFile(remoteFile); } else { String stringValue = getString(columnIndex); debug("AsciiStream in String!"); try { // Put back clean CR_LF BufferedReader bufferedReader = new BufferedReader(new StringReader(stringValue)); StringWriter stringWriter = new StringWriter(); String line = null; while ((line = bufferedReader.readLine()) != null) { stringWriter.write(line + CR_LF); } String cleaned = stringWriter.toString(); byte[] bytes = cleaned.getBytes(); in = new ByteArrayInputStream(bytes); } catch (Exception e) { throw new SQLException(e.getMessage(), e); } } return in; } /** * * Return the InputStream corresponding to a getAsciiStream * * @param remoteFile * the remote file eventually html encoded * @return the eventually html decoded ascii input stream */ private InputStream getAsciiInputStreamFromRemoteFile(String remoteFile) throws SQLException { debug("begin: " + remoteFile); InputStream in = null; try { File localFile = ClobHttp.createUniqueClobFile(); downloadBlobClob(localFile, remoteFile); debug("localFile: " + localFile); if (localFile.length() == TransportConverter.KAWANFW_STREAM_NULL.length()) { debug("before content"); String content = FileUtils.readFileToString(localFile); debug("content: " + content); // Return null if the file content is // TransportConverter.KAWANFW_STREAM_NULL if (content.contains(TransportConverter.KAWANFW_STREAM_NULL)) { localFiles.add(localFile); return null; } } debug("localFile: " + localFile); in = new BufferedInputStream(new FileInputStream(getLocalFileHtmlDecoded(localFile))); } catch (Exception e) { JdbcHttpTransferUtil.wrapExceptionAsSQLException(e); } return in; } /** * * Html decode the text file is necessary * * @param localFile * the local text file * @return a new local text file but h that is html decoded if necessary * @throws FileNotFoundException * @throws IOException */ private File getLocalFileHtmlDecoded(File localFile) throws FileNotFoundException, IOException { if (connectionHttp.isHtmlEncodingOn()) { localFiles.add(localFile); return localFile; } File localFileDecoded = new File(localFile.toString() + HTML_DECODED); localFiles.add(localFileDecoded); BufferedReader br = null; Writer writer = null; try { br = new BufferedReader(new FileReader(localFile)); writer = new BufferedWriter(new FileWriter(localFileDecoded)); String line = null; while ((line = br.readLine()) != null) { line = HtmlConverter.fromHtml(line); writer.write(line + CR_LF); } } finally { IOUtils.closeQuietly(br); IOUtils.closeQuietly(writer); if (!KeepTempFilePolicyParms.KEEP_TEMP_FILE && !DEBUG) { localFile.delete(); } } return localFileDecoded; } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a stream of uninterpreted bytes. The * value can then be read in chunks from the stream. This method is * particularly suitable for retrieving large <code>LONGVARBINARY</code> * values. * * <P> * <B>Note:</B> All the data in the returned stream must be read prior to * getting the value of any other column. The next call to a getter method * implicitly closes the stream. Also, a stream may return <code>0</code> * when the method <code>InputStream.available</code> is called whether * there is data available or not. * * @param columnIndex * the first column is 1, the second is 2, ... * @return a Java input stream that delivers the database column value as a * stream of uninterpreted bytes; if the value is SQL * <code>NULL</code>, the value returned is <code>null</code> * @exception SQLException * if the columnIndex is not valid; if a database access * error occurs or this method is called on a closed result * set */ @Override public java.io.InputStream getBinaryStream(int columnIndex) throws SQLException { testIfClosed(); return getInputStreamOfMap(columnIndex); } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a stream of uninterpreted * <code>byte</code>s. The value can then be read in chunks from the stream. * This method is particularly suitable for retrieving large * <code>LONGVARBINARY</code> values. * * <P> * <B>Note:</B> All the data in the returned stream must be read prior to * getting the value of any other column. The next call to a getter method * implicitly closes the stream. Also, a stream may return <code>0</code> * when the method <code>available</code> is called whether there is data * available or not. * * @param columnLabel * the label for the column specified with the SQL AS clause. If * the SQL AS clause was not specified, then the label is the * name of the column * @return a Java input stream that delivers the database column value as a * stream of uninterpreted bytes; if the value is SQL * <code>NULL</code>, the result is <code>null</code> * @exception SQLException * if the columnLabel is not valid; if a database access * error occurs or this method is called on a closed result * set */ @Override public java.io.InputStream getBinaryStream(String columnLabel) throws SQLException { testIfClosed(); int columnIndex = findColumn(columnLabel); return getBinaryStream(columnIndex); } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>Blob</code> object in the Java * programming language. * * @param i * the first column is 1, the second is 2, ... * @return a <code>Blob</code> object representing the SQL <code>BLOB</code> * value in the specified column * @exception SQLException * if a database access error occurs * @since 1.2 */ @Override public Blob getBlob(int i) throws SQLException { testIfClosed(); InputStream in = getBinaryStream(i); if (in == null) { return null; } else { Blob blob = new BlobHttp(getBinaryStream(i), connectionHttp); return blob; } } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>Blob</code> object in the Java * programming language. * * @param colName * the name of the column from which to retrieve the value * @return a <code>Blob</code> object representing the SQL <code>BLOB</code> * value in the specified column * @exception SQLException * if a database access error occurs * @since 1.2 */ @Override public Blob getBlob(String colName) throws SQLException { int columnIndex = findColumn(colName); return getBlob(columnIndex); } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>Clob</code> object in the Java * programming language. * * @param i * the first column is 1, the second is 2, ... * @return a <code>Clob</code> object representing the SQL <code>CLOB</code> * value in the specified column * @exception SQLException * if a database access error occurs * @since 1.2 */ @Override public Clob getClob(int i) throws SQLException { testIfClosed(); Clob clob = new ClobHttp(this.getCharacterStream(i)); return clob; } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>Clob</code> object in the Java * programming language. * * @param colName * the name of the column from which to retrieve the value * @return a <code>Clob</code> object representing the SQL <code>CLOB</code> * value in the specified column * @exception SQLException * if a database access error occurs * @since 1.2 */ @Override public Clob getClob(String colName) throws SQLException { return getClob(findColumn(colName)); } /** * Test if a result set is still open * * @throws SQLException * it the Connection is closed */ private void testIfClosed() throws SQLException { if (isClosed()) { throw new SQLException("This ResultSet is closed!"); } } /** * Releases this <code>ResultSet</code> object's database and JDBC resources * immediately instead of waiting for this to happen when it is * automatically closed. * * <P> * <B>Note:</B> A <code>ResultSet</code> object is automatically closed by * the <code>Statement</code> object that generated it when that * <code>Statement</code> object is closed, re-executed, or is used to * retrieve the next result from a sequence of multiple results. A * <code>ResultSet</code> object is also automatically closed when it is * garbage collected. * * @exception SQLException * if a database access error occurs */ @Override public void close() throws SQLException { super.close(); // debug("resultSetMetaDataMap: " + resultSetMetaDataMap); resultSetMetaDataMap.remove(this); // debug("resultSetMetaDataMap: " + resultSetMetaDataMap); if (KeepTempFilePolicyParms.KEEP_TEMP_FILE || DEBUG) { return; } try { if (list != null) { debug("Before list.clear()"); list.clear(); // To close and delete the underlying // file. // MANDATORY FOR CLOSE. debug("After list.clear()"); } if (rsMetaDataFileTypeFunction != null) { rsMetaDataFileTypeFunction.delete(); } if (rsMetaDataFileTypeNormal != null) { rsMetaDataFileTypeNormal.delete(); } // Delete all the files in the list if (localFiles != null) { for (File localFile : localFiles) { boolean deleted = localFile.delete(); if (!deleted) { debug("localFile not deleted: " + localFile); } } } // Delete all the remotes files in the list // if (remoteFiles != null) { // FileSession fileSession = connectionHttp.getFileSession(); // for (String remoteFile : remoteFiles) { // boolean deleted = fileSession.delete(remoteFile); // // if (!deleted) { // debug("remoteFile not deleted: " + remoteFile); // } // } // } if (remoteFiles != null) { RemoteSession remoteSession = connectionHttp.getRemoteSession(); for (String remoteFile : remoteFiles) { // boolean deleted = fileSession.delete(remoteFile); boolean deleted = new RemoteFile(remoteSession, remoteFile).delete(); if (!deleted) { debug("remoteFile not deleted: " + remoteFile); } } } } catch (Exception e) { // we do not care e.printStackTrace(); } finally { statementHolder = null; localFiles = null; colPosition = null; list = null; rsMetaDataFileTypeFunction = null; } } /** * Reports whether the last column read had a value of SQL <code>NULL</code> * . Note that you must first call one of the getter methods on a column to * try to read its value and then call the method <code>wasNull</code> to * see if the value read was SQL <code>NULL</code>. * * @return <code>true</code> if the last column value read was SQL * <code>NULL</code> and <code>false</code> otherwise * @exception SQLException * if a database access error occurs */ @Override public boolean wasNull() throws SQLException { testIfClosed(); return wasNull; } /** * Retrieves the type of this <code>ResultSet</code> object. The type is * determined by the <code>Statement</code> object that created the result * set. * * @return <code>ResultSet.TYPE_FORWARD_ONLY</code>, * <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or * <code>ResultSet.TYPE_SCROLL_SENSITIVE</code> * @exception SQLException * if a database access error occurs * @since 1.2 */ public int getType() throws SQLException { testIfClosed(); return this.resultSetType; } /** * Retrieves the concurrency mode of this <code>ResultSet</code> object. The * concurrency used is determined by the <code>Statement</code> object that * created the result set. * * @return the concurrency type, either * <code>ResultSet.CONCUR_READ_ONLY</code> or * <code>ResultSet.CONCUR_UPDATABLE</code> * @exception SQLException * if a database access error occurs * @since 1.2 */ public int getConcurrency() throws SQLException { testIfClosed(); return this.resultSetConcurrency; } /** * Retrieves the holdability of this <code>ResultSet</code> object * * @return either <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or * <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code> * @throws SQLException * if a database access error occurs or this method is called on * a closed result set * @since 1.6 */ public int getHoldability() throws SQLException { testIfClosed(); return this.resultSetHoldability; } }