org.kawanfw.sql.jdbc.ResultSetHttp.java Source code

Java tutorial

Introduction

Here is the source code for org.kawanfw.sql.jdbc.ResultSetHttp.java

Source

/*
 * 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;
    }

}