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

Java tutorial

Introduction

Here is the source code for org.kawanfw.sql.jdbc.ConnectionHttp.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.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLWarning;
import java.sql.Savepoint;
import java.sql.Statement;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;

import org.apache.commons.io.FileUtils;
import org.kawanfw.commons.api.client.SessionParameters;
import org.kawanfw.commons.client.http.HttpTransfer;
import org.kawanfw.commons.jdbc.abstracts.AbstractConnection;
import org.kawanfw.commons.util.ClientLogger;
import org.kawanfw.commons.util.DefaultParms;
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.RemoteSession;
import org.kawanfw.file.api.util.client.FilesTransferWithProgress;
import org.kawanfw.sql.jdbc.http.JdbcHttpConnectionInfoTransfer;
import org.kawanfw.sql.jdbc.http.JdbcHttpMetaDataTransfer;
import org.kawanfw.sql.jdbc.http.JdbcHttpSavepointTransfer;
import org.kawanfw.sql.jdbc.http.JdbcHttpStatementTransfer;
import org.kawanfw.sql.jdbc.http.JdbcHttpTransactionTransfer;
import org.kawanfw.sql.jdbc.http.JdbcHttpTransferUtil;
import org.kawanfw.sql.jdbc.util.JdbcUtil;
import org.kawanfw.sql.jdbc.util.StatementHolderFileList;
import org.kawanfw.sql.jdbc.util.zip.HttpTransferZip;
import org.kawanfw.sql.json.StatementHolder;
import org.kawanfw.sql.json.no_obfuscation.DatabaseMetaDataHolder;
import org.kawanfw.sql.json.no_obfuscation.DatabaseMetaDataHolderTransport;
import org.kawanfw.sql.util.JdbcUrlHeader;
import org.kawanfw.sql.version.Version;

/**
 * Creates and handle a HTTP Connection to SQLExecutor on Http Server.
 */

public class ConnectionHttp extends AbstractConnection implements Connection, Cloneable {

    public static final String FEATURE_NOT_SUPPORTED_IN_STATELESS_MODE = Tag.PRODUCT
            + "This feature is not supported in stateless mode";

    public static final String KAWANFW_NOT_SUPPORTED_METHOD = Tag.PRODUCT + "Method is not yet implemented.";

    /** Set to true to display/log debug info */
    private static boolean DEBUG = FrameworkDebug.isSet(ConnectionHttp.class);

    /** The default maximum number of statement for transport in memory */
    private static int DEFAULT_MAX_STATEMENTS_FOR_MEMORY_TRANSPORT = 100;

    /**
     * Map that stores DatabaseMetaData per Connection to avoid server
     * re-contact if programmer calls many times Connection.getMetaData()
     * instead of reusing the result value.
     */
    private static Map<Connection, DatabaseMetaData> databaseMetaDataMap = new HashMap<Connection, DatabaseMetaData>();

    /** The username */
    private String username = null;

    /** The Authentication Token */
    private String authenticationToken = null;

    /** The current RemoteSession instance used for file transfers */
    private RemoteSession remoteSession = null;

    /** The host to use */
    private String url = null;

    /** Proxy to use with HttpUrlConnection */
    private Proxy proxy = null;

    /** For authenticated proxy */
    private PasswordAuthentication passwordAuthentication = null;

    /** The Http Parameters instance */
    private SessionParameters sessionParameters = null;

    /**
     * Set to true if the user has closed the connection by a explicit call to
     * close()
     */
    private boolean isClosed = false;

    /**
     * The supported holdability is only CLOSE_CURSORS_AT_COMMIT in
     * statelessMode
     */
    private int holdability = ResultSet.CLOSE_CURSORS_AT_COMMIT;

    /** Autocommit local state */
    private boolean autoCommit = true;

    /** The holdability, false if user does not change it */
    private boolean readOnly = false;

    /** The transaction isolation level, -1 if user does not change it */
    private int transactionIsolation = -1;

    /** The list of statement holder are stored on a file */
    private StatementHolderFileList statementHolderFileList = null;

    /** The maximum number of statement for transport in memory */
    private int maxStatementsForMemoryTransport = DEFAULT_MAX_STATEMENTS_FOR_MEMORY_TRANSPORT;

    /**
     * if true, the Statement Parameters will be encrypted if the
     * HttpProtocolParameter is set
     */
    private boolean encryptStatementParameters = false;

    /** Progress value between 0 and 100. Will be used by progress indicators. */
    private AtomicInteger progress = new AtomicInteger();

    /** Says if user has cancelled the blob/clob upload or download */
    private AtomicBoolean cancelled = new AtomicBoolean();

    /**
     * The list of local files to uplaod and delete (in fire and forget mode) at
     * commit
     */
    List<File> localFiles = new Vector<File>();

    /**
     * The list of input streams to delete (in fire and forget mode) at commit
     */
    List<InputStream> localInputStreams = new Vector<InputStream>();

    /** The length of each InputStream */
    List<Long> localInputStreamLengths = new Vector<Long>();

    /**
     * The list of remote uploaded files delete (in fire and forget mode) at at
     * commit
     */
    List<String> remoteFiles = new Vector<String>();

    /**
     * The result of the transfered ExecuteUpdate (last result if aucoCommit is
     * off)
     */
    String receiveFromExecuteUpdate = null;

    //
    // New fields for server stateless or connected mode
    //

    /** Says if we gare in stateless mode */
    private boolean statelessMode = false;

    /** The incremented number of Connections */
    private static int MAX_CONNECTION_NUMBER = 0;

    /** The connectionId Id to use */
    private String connectionId = null;

    /** The unique instance for transfer */
    private HttpTransfer httpTransfer = null;

    /**
     * Says if ResultSet Meta Data must be downloaded from server along with
     * ResultSet
     */
    private boolean joinResultSetMetaData = false;

    /** Says if Result Set must be GZIP before download */
    private boolean zipResultSet;

    /**
     * Public constructor for clone() - Must be public because RemoteConnection
     * uses now composition)
     * 
     * @param url
     *            the URL of the path to the ServerSqlManager Servlet
     * @param username
     *            the user username
     * @param proxy
     *            the proxy to use, null for direct access
     * @param passwordAuthentication
     *            the proxy credentials, null if no proxy or if the proxy does
     *            not require authentication
     * @param sessionParameters
     *            the http protocol parameters to set (maybe null if none)
     * @param remoteSession
     *            the actual AceQL FILE Session
     * @param statelessMode
     *            says if server is stateless for client side
     * @param joinResultSetMetaData
     *            says if metadata must be downloaded along result set
     * @param zipResultSet
     *            says if ResultSet must be zipped by server size
     */

    public ConnectionHttp(String url, String username, Proxy proxy, PasswordAuthentication passwordAuthentication,
            SessionParameters sessionParameters, RemoteSession remoteSession, boolean statelessMode,
            boolean joinResultSetMetaData, boolean zipResultSet) {

        this.url = url;
        this.username = username;
        this.proxy = proxy;
        this.passwordAuthentication = passwordAuthentication;
        this.sessionParameters = sessionParameters;

        this.remoteSession = remoteSession.clone();
        this.authenticationToken = this.remoteSession.getAuthenticationToken();

        // The file that will contain the statements, one per line added
        this.statementHolderFileList = new StatementHolderFileList(this);

        this.statelessMode = statelessMode;

        this.joinResultSetMetaData = joinResultSetMetaData;
        this.zipResultSet = zipResultSet;

        String urlHttpOnly = JdbcUrlHeader.getUrlHttpOnly(url);
        httpTransfer = new HttpTransferZip(urlHttpOnly, proxy, passwordAuthentication, sessionParameters);

        if (statelessMode) {
            this.connectionId = "0"; // Important , will be transmitted to
            // server
        } else {
            this.connectionId = buildConnectionId();
            JdbcHttpTransactionTransfer jdbcHttpTransactionTransfer = new JdbcHttpTransactionTransfer(this,
                    authenticationToken);

            try {
                jdbcHttpTransactionTransfer.initRemoteConnection();
            } catch (SQLException e) {
                throw new IllegalStateException(e);
            }
        }

    }

    /**
     * Constructor Build the SQL/JDBC http connection with a proxy and http
     * protocol parameters, if necessary.
     * 
     * @param url
     *            the URL of the path to the ServerSqlManager Servlet
     * @param username
     *            the user username
     * @param password
     *            the authentication password for username
     * @param proxy
     *            the proxy to use, null for direct access
     * @param passwordAuthentication
     *            the proxy credentials, null if proxy does not require
     *            authentication
     * @param sessionParameters
     *            the http protocol parameters to set (maybe null if none)
     * @param statelessMode
     *            if true, we are in stateless mode on the server
     * @param joinResultSetMetaData
     *            if true, ResultSet.getMetaData() will be downloaded along with
     *            ResultSet when a SELECT is sent to the server
     * @param zipResultSet
     *            says if ResultSet must be zipped by server size
     * @throws IllegalArgumentException
     *             if url, username, password is null
     * @throws SQLException
     *             if any <code>Exception</code> occurs. The
     *             <code>SQLException</code> wraps the original
     *             <code>Exception</code> that may be accessed using
     *             {@link SQLException#getCause()}.
     * 
     */
    public ConnectionHttp(String url, String username, char[] password, Proxy proxy,
            PasswordAuthentication passwordAuthentication, SessionParameters sessionParameters,
            boolean statelessMode, boolean joinResultSetMetaData, boolean zipResultSet) throws SQLException {

        if (username == null) {
            throw new IllegalArgumentException("username can not be null!");
        }

        if (password == null) {
            throw new IllegalArgumentException("password can not be null!");
        }

        if (url == null) {
            throw new IllegalArgumentException("url can not be null!");
        }

        try {
            this.url = url;
            this.proxy = proxy;
            this.passwordAuthentication = passwordAuthentication;
            this.sessionParameters = sessionParameters;
            this.username = username;

            // The file that will contain the statements, one per line added
            this.statementHolderFileList = new StatementHolderFileList(this);

            String urlHttpOnly = JdbcUrlHeader.getUrlHttpOnly(url);
            // Do the current username on the SQl Servlet
            remoteSession = new RemoteSession(urlHttpOnly, username, password, proxy, passwordAuthentication,
                    sessionParameters);

            // Because remoteSession.url may have been updated from http://
            // to https://, update this.url:
            // this.url = JdbcParms.JDBC_URL_HEADER + remoteSession.getUrl();
            this.url = JdbcUrlHeader.prefixUrlWithJdbcProductName(remoteSession.getUrl());
            urlHttpOnly = JdbcUrlHeader.getUrlHttpOnly(url);

            this.authenticationToken = remoteSession.getAuthenticationToken();
            this.statelessMode = statelessMode;

            this.joinResultSetMetaData = joinResultSetMetaData;
            this.zipResultSet = zipResultSet;

            httpTransfer = new HttpTransferZip(urlHttpOnly, proxy, passwordAuthentication, sessionParameters);

            if (statelessMode) {
                this.connectionId = "0"; // Important , will be transmitted to
                // server
                // if stateless mode ==> no chunking
                // ==> force sessionParameters.setUploadChunkLength(0)
                if (this.sessionParameters != null) {
                    this.sessionParameters.setUploadChunkLength(0);
                }
            } else {
                this.connectionId = buildConnectionId();
                JdbcHttpTransactionTransfer jdbcHttpTransactionTransfer = new JdbcHttpTransactionTransfer(this,
                        authenticationToken);

                try {
                    jdbcHttpTransactionTransfer.initRemoteConnection();
                } catch (SQLException e) {
                    throw new IllegalStateException(e);
                }
            }

        } catch (Exception e) {
            JdbcHttpTransferUtil.wrapExceptionAsSQLException(e);
        }

    }

    /**
     * Returns the cancelled value set by the progress indicator
     * 
     * @return the cancelled value set by the progress indicator
     * 
     * @since 1.8
     */
    public AtomicBoolean getCancelled() {
        return cancelled;
    }

    /**
     * Sets the shareable canceled variable that will be used by the progress
     * indicator to notify this instance that the user has cancelled the current
     * blob/clob upload or download.
     * 
     * @param the
     *            shareable canceled variable that will be used by the progress
     *            indicator to notify this instance that the end user has
     *            cancelled the current blob/clob upload or download
     * 
     * @since 1.8
     */
    public void setCancelled(AtomicBoolean cancelled) {
        this.cancelled = cancelled;
    }

    /**
     * Returns the shareable progress variable that will store blob/clob upload
     * or download progress between 0 and 100
     * 
     * @return the shareable progress variable that will store blob/clob upload
     *         or download progress between 0 and 100
     * 
     * @since 1.8
     */
    public AtomicInteger getProgress() {
        return progress;
    }

    /**
     * Sets the shareable progress variable that will store blob/clob upload or
     * download progress between 0 and 100. Will be used by progress indicators
     * to show the progress.
     * 
     * @param progress
     *            the shareable progress variable
     * 
     * @since 1.8
     */
    public void setProgress(AtomicInteger progress) {
        this.progress = progress;
    }

    /**
     * @return the httpTransfer
     */
    public HttpTransfer getHttpTransfer() {
        return httpTransfer;
    }

    /**
     * Returns the boolean that says if server ResultSet Meta Data must be
     * downloaded along with the server ResultSet
     * 
     * @return {@code true} if server ResultSet Meta Data must be downloaded along with
     *         the server ResultSet, else {@code false}
     * @since 3.0
     */
    public boolean isJoinResultSetMetaData() {
        return joinResultSetMetaData;
    }

    /**
     * Returns the boolean that says if server {@code ResultSet} must be GZIPed
     * before download.
     * 
     * @return {@code true} if server {@code ResultSet} must be GZIPed before download
     * 
     * @since 4.1
     */
    public boolean isZipResultSet() {
        return zipResultSet;
    }

    /**
     * Says if  server {@code ResultSet} must be GZIPed before download
     * @param {@code true} if server {@code ResultSet} must be GZIPed before download, else {@code false}
     * 
     * Defaults to {@code true}
     * @since 4.1
     */
    public void setZipResultSet(boolean zipResultSet) {
        this.zipResultSet = zipResultSet;
    }

    /**
     * Generate a new incremented Transaction Id
     * 
     * @return the new incremented Transaction Id
     */
    private static synchronized int getNextConnectionNumber() {
        MAX_CONNECTION_NUMBER++;
        return MAX_CONNECTION_NUMBER;
    }

    /**
     * Build a unique transaction Id for this username
     * 
     * @return the built transaction Id
     */
    private String buildConnectionId() {
        String connectionId = getNextConnectionNumber() + "_" + JdbcUtil.getMacAddress();
        return connectionId;
    }

    /**
     * Says if Html Encoding is on for Clobs uploads/downloads
     * 
     * @return true if Html Encoding is on
     */
    boolean isHtmlEncodingOn() {
        boolean htmlEncoding = DefaultParms.DEFAULT_HTML_ENCODING_ON;
        if (sessionParameters != null) {
            htmlEncoding = sessionParameters.isHtmlEncodingOn();
        }
        return htmlEncoding;
    }

    /**
     * add a StatementHolder to the StatementHolder list to execute
     * 
     * @param statementHolder
     *            the StatementHolder to add to the StatementHolder list to
     *            execute
     */
    void addStatementHolder(StatementHolder statementHolder) throws SQLException {
        testIfClosed();
        this.statementHolderFileList.add(statementHolder);
    }

    /**
     * Reset the statement holder list
     */
    void resetStatementHolderList() throws SQLException {
        testIfClosed();
        this.statementHolderFileList.delete(); // closes and delete the file
        this.statementHolderFileList = new StatementHolderFileList(this);

    }

    /**
     * Returns the maximum length authorized for a string
     * 
     * @return
     */
    int getMaxLengthForString() {

        int maxLengthForString = DefaultParms.DEFAULT_MAX_LENGTH_FOR_STRING;
        if (sessionParameters != null) {
            maxLengthForString = sessionParameters.getMaxLengthForString();
        }
        return maxLengthForString;
    }

    /**
     * Upload all the blob parameters
     */
    private void uploadBlobParameters() throws SQLException {

        // reinit progress
        progress.set(0);
        long totalLength = getLocalTotalLength();

        try {
            // for (int i = 0; i < localFiles.size(); i++) {
            // // Do the upload
            // debug("uploading file: " + localFiles.get(i));
            // remoteSession.getFileTransferWrapper().upload(localFiles.get(i),
            // remoteFiles.get(i));
            // }

            FilesTransferWithProgress filesTransferWithProgress = new FilesTransferWithProgress(this.remoteSession,
                    this.progress, this.cancelled);
            filesTransferWithProgress.upload(localFiles, remoteFiles, totalLength);

            // 1) Upoad files
            if (!localFiles.isEmpty()) {
                filesTransferWithProgress.upload(localFiles, remoteFiles, totalLength);
            }

            // 2) upload InputStreams
            if (!localInputStreams.isEmpty()) {
                filesTransferWithProgress.upload(localInputStreams, localInputStreamLengths, remoteFiles,
                        totalLength);
            }

        } catch (Exception e) {
            JdbcHttpTransferUtil.wrapExceptionAsSQLException(e);
        } finally {
            // NO! We want to repeat the uploads, so stay at 99
            // this.progress.set(100);
            if (!KeepTempFilePolicyParms.KEEP_TEMP_FILE && !DEBUG) {
                if (localFiles != null) {
                    for (File localFile : localFiles) {
                        localFile.delete();
                    }
                }
            }
        }

    }

    /**
     * Get the total length of the files to upload
     * 
     * @return the total length of the files to upload
     */
    private long getLocalTotalLength() {
        long totalLength = 0;
        for (File localFile : localFiles) {
            totalLength += localFile.length();
        }
        for (Long localLength : localInputStreamLengths) {
            totalLength += localLength;
        }

        return totalLength;
    }

    /**
     * Test if user has hit cancel. if yes, throw a wrapped
     * HttpTransferInterruptedException
     * 
     * @throws SQLException
     *             the wrapped InterruptedException if user has hit cancel
     */
    public void testIfUploadInterrupted() throws SQLException {
        if (cancelled.get()) {
            throw new SQLException(new InterruptedException(Tag.PRODUCT + " File upload interrupted by user."));
        }
    }

    /**
     * Execute a remote sql execute update statement and get the string
     * 
     * @param sql
     * @return the result of the execute update
     */
    synchronized String getStringFromExecuteUpdateListOnServer() throws SQLException {
        testIfClosed();

        if (isStatelessMode()) {
            try {
                // Upload each blob/clob parameters
                if (!localFiles.isEmpty()) {
                    this.uploadBlobParameters();
                }

            } finally {
                this.localFiles = new Vector<File>();
                this.remoteFiles = new Vector<String>();
            }
        }

        testIfUploadInterrupted();

        JdbcHttpStatementTransfer jdbcHttpStatementTransfer = new JdbcHttpStatementTransfer(this,
                authenticationToken);
        String result = jdbcHttpStatementTransfer.getStringFromExecuteUpdateListOnServer(statementHolderFileList);

        testIfUploadInterrupted();

        return result;

    }

    /**
     * Creates a <code>Statement</code> object for sending SQL statements to the
     * database. SQL statements without parameters are normally executed using
     * <code>Statement</code> objects. If the same SQL statement is executed
     * many times, it may be more efficient to use a
     * <code>PreparedStatement</code> object.
     * <P>
     * Result sets created using the returned <code>Statement</code> object will
     * by default be type <code>TYPE_FORWARD_ONLY</code> and have a concurrency
     * level of <code>CONCUR_READ_ONLY</code>.
     * 
     * @return a new default <code>Statement</code> object
     * @exception SQLException
     *                if a database access error occurs
     */
    @Override
    public Statement createStatement() throws SQLException {
        testIfClosed();

        return new StatementHttp(this, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
                ResultSet.CLOSE_CURSORS_AT_COMMIT);
    }

    /**
     * Creates a <code>Statement</code> object that will generate
     * <code>ResultSet</code> objects with the given type and concurrency. This
     * method is the same as the <code>createStatement</code> method above, but
     * it allows the default result set type and concurrency to be overridden.
     * The holdability of the created result sets can be determined by calling
     * {@link #getHoldability}.
     * 
     * @param resultSetType
     *            a result set type; one of
     *            <code>ResultSet.TYPE_FORWARD_ONLY</code>,
     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or
     *            <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
     * @param resultSetConcurrency
     *            a concurrency type; one of
     *            <code>ResultSet.CONCUR_READ_ONLY</code> or
     *            <code>ResultSet.CONCUR_UPDATABLE</code>
     * @return a new <code>Statement</code> object that will generate
     *         <code>ResultSet</code> objects with the given type and
     *         concurrency
     * @exception SQLException
     *                if a database access error occurs, this method is called
     *                on a closed connection or the given parameters are not
     *                <code>ResultSet</code> constants indicating type and
     *                concurrency
     * @exception SQLFeatureNotSupportedException
     *                if the JDBC driver does not support this method or this
     *                method is not supported for the specified result set type
     *                and result set concurrency.
     * @since 1.2
     */
    @Override
    public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
        testIfClosed();

        // We support only ResultSet.CONCUR_READ_ONLY
        if (resultSetConcurrency == ResultSet.CONCUR_UPDATABLE) {
            throw new SQLFeatureNotSupportedException("Concurrency ResultSet.CONCUR_UPDATABLE is not supported.");
        }

        return new StatementHttp(this, resultSetType, resultSetConcurrency, getHoldability());
    }

    /**
     * Creates a <code>Statement</code> object that will generate
     * <code>ResultSet</code> objects with the given type, concurrency, and
     * holdability. This method is the same as the <code>createStatement</code>
     * method above, but it allows the default result set type, concurrency, and
     * holdability to be overridden.
     * 
     * @param resultSetType
     *            one of the following <code>ResultSet</code> constants:
     *            <code>ResultSet.TYPE_FORWARD_ONLY</code>,
     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or
     *            <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
     * @param resultSetConcurrency
     *            one of the following <code>ResultSet</code> constants:
     *            <code>ResultSet.CONCUR_READ_ONLY</code> or
     *            <code>ResultSet.CONCUR_UPDATABLE</code>
     * @param resultSetHoldability
     *            one of the following <code>ResultSet</code> constants:
     *            <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or
     *            <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>
     * @return a new <code>Statement</code> object that will generate
     *         <code>ResultSet</code> objects with the given type, concurrency,
     *         and holdability
     * @exception SQLException
     *                if a database access error occurs, this method is called
     *                on a closed connection or the given parameters are not
     *                <code>ResultSet</code> constants indicating type,
     *                concurrency, and holdability
     * @exception SQLFeatureNotSupportedException
     *                if the JDBC driver does not support this method or this
     *                method is not supported for the specified result set type,
     *                result set holdability and result set concurrency.
     * @see ResultSet
     * @since 1.4
     */
    @Override
    public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
            throws SQLException {
        testIfClosed();

        // We support only ResultSet.CONCUR_READ_ONLY
        if (resultSetConcurrency == ResultSet.CONCUR_UPDATABLE) {
            throw new SQLFeatureNotSupportedException("Concurrency ResultSet.CONCUR_UPDATABLE is not supported.");
        }

        // We support only ResultSet.CLOSE_CURSORS_AT_COMMIT
        if (resultSetHoldability == ResultSet.HOLD_CURSORS_OVER_COMMIT) {
            throw new SQLFeatureNotSupportedException(
                    "Concurrency ResultSet.HOLD_CURSORS_OVER_COMMIT is not supported.");
        }

        return new StatementHttp(this, resultSetType, resultSetConcurrency, resultSetHoldability);
    }

    /**
     * Creates a <code>CallableStatement</code> object for calling database
     * stored procedures. The <code>CallableStatement</code> object provides
     * methods for setting up its IN and OUT parameters, and methods for
     * executing the call to a stored procedure.
     * 
     * <P>
     * <B>Note:</B> This method is optimized for handling stored procedure call
     * statements. Some drivers may send the call statement to the database when
     * the method <code>prepareCall</code> is done; others may wait until the
     * <code>CallableStatement</code> object is executed. This has no direct
     * effect on users; however, it does affect which method throws certain
     * SQLExceptions.
     * <P>
     * Result sets created using the returned <code>CallableStatement</code>
     * object will by default be type <code>TYPE_FORWARD_ONLY</code> and have a
     * concurrency level of <code>CONCUR_READ_ONLY</code>.
     * 
     * @param sql
     *            an SQL statement that may contain one or more '?' parameter
     *            placeholders. Typically this statement is a JDBC function call
     *            escape string.
     * @return a new default <code>CallableStatement</code> object containing
     *         the pre-compiled SQL statement
     * @exception SQLException
     *                if a database access error occurs
     */
    @Override
    public CallableStatement prepareCall(String sql) throws SQLException {

        testIfClosed();

        return new CallableStatementHttp(this, sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
                ResultSet.CLOSE_CURSORS_AT_COMMIT);
    }

    /**
     * Creates a <code>CallableStatement</code> object that will generate
     * <code>ResultSet</code> objects with the given type and concurrency. This
     * method is the same as the <code>prepareCall</code> method above, but it
     * allows the default result set type and concurrency to be overridden.
     * 
     * @param sql
     *            a <code>String</code> object that is the SQL statement to be
     *            sent to the database; may contain on or more ? parameters
     * @param resultSetType
     *            a result set type; one of
     *            <code>ResultSet.TYPE_FORWARD_ONLY</code>,
     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or
     *            <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
     * @param resultSetConcurrency
     *            a concurrency type; one of
     *            <code>ResultSet.CONCUR_READ_ONLY</code> or
     *            <code>ResultSet.CONCUR_UPDATABLE</code>
     * @return a new <code>CallableStatement</code> object containing the
     *         pre-compiled SQL statement that will produce
     *         <code>ResultSet</code> objects with the given type and
     *         concurrency
     * @exception SQLException
     *                if a database access error occurs or the given parameters
     *                are not <code>ResultSet</code> constants indicating type
     *                and concurrency
     * @since 1.2
     */
    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency)
            throws SQLException {

        testIfClosed();

        // We support only ResultSet.CONCUR_READ_ONLY
        if (resultSetConcurrency == ResultSet.CONCUR_UPDATABLE) {
            throw new SQLFeatureNotSupportedException("Concurrency ResultSet.CONCUR_UPDATABLE is not supported.");
        }

        return new CallableStatementHttp(this, sql, resultSetType, resultSetConcurrency, getHoldability());
    }

    /**
     * Creates a <code>CallableStatement</code> object that will generate
     * <code>ResultSet</code> objects with the given type and concurrency. This
     * method is the same as the <code>prepareCall</code> method above, but it
     * allows the default result set type, result set concurrency type and
     * holdability to be overridden.
     * 
     * @param sql
     *            a <code>String</code> object that is the SQL statement to be
     *            sent to the database; may contain on or more ? parameters
     * @param resultSetType
     *            one of the following <code>ResultSet</code> constants:
     *            <code>ResultSet.TYPE_FORWARD_ONLY</code>,
     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or
     *            <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
     * @param resultSetConcurrency
     *            one of the following <code>ResultSet</code> constants:
     *            <code>ResultSet.CONCUR_READ_ONLY</code> or
     *            <code>ResultSet.CONCUR_UPDATABLE</code>
     * @param resultSetHoldability
     *            one of the following <code>ResultSet</code> constants:
     *            <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or
     *            <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>
     * @return a new <code>CallableStatement</code> object, containing the
     *         pre-compiled SQL statement, that will generate
     *         <code>ResultSet</code> objects with the given type, concurrency,
     *         and holdability
     * @exception SQLException
     *                if a database access error occurs or the given parameters
     *                are not <code>ResultSet</code> constants indicating type,
     *                concurrency, and holdability
     * @see ResultSet
     * @since 1.4
     */
    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,
            int resultSetHoldability) throws SQLException {
        testIfClosed();

        // We support only ResultSet.CONCUR_READ_ONLY
        if (resultSetConcurrency == ResultSet.CONCUR_UPDATABLE) {
            throw new SQLFeatureNotSupportedException("Concurrency ResultSet.CONCUR_UPDATABLE is not supported.");
        }

        // We support only ResultSet.CLOSE_CURSORS_AT_COMMIT
        if (resultSetHoldability == ResultSet.HOLD_CURSORS_OVER_COMMIT) {
            throw new SQLFeatureNotSupportedException(
                    "Concurrency ResultSet.HOLD_CURSORS_OVER_COMMIT is not supported.");
        }

        return new CallableStatementHttp(this, sql, resultSetType, resultSetConcurrency, resultSetHoldability);

    }

    /**
     * Creates a <code>PreparedStatement</code> object for sending parameterized
     * SQL statements to the database.
     * <P>
     * A SQL statement with or without IN parameters can be pre-compiled and
     * stored in a <code>PreparedStatement</code> object. This object can then
     * be used to efficiently execute this statement multiple times.
     * 
     * <P>
     * <B>Note:</B> This method is optimized for handling parametric SQL
     * statements that benefit from precompilation. If the driver supports
     * precompilation, the method <code>prepareStatement</code> will send the
     * statement to the database for precompilation. Some drivers may not
     * support precompilation. In this case, the statement may not be sent to
     * the database until the <code>PreparedStatement</code> object is executed.
     * This has no direct effect on users; however, it does affect which methods
     * throw certain <code>SQLException</code> objects.
     * <P>
     * Result sets created using the returned <code>PreparedStatement</code>
     * object will by default be type <code>TYPE_FORWARD_ONLY</code> and have a
     * concurrency level of <code>CONCUR_READ_ONLY</code>.
     * 
     * @param sql
     *            an SQL statement that may contain one or more '?' IN parameter
     *            placeholders
     * @return a new default <code>PreparedStatement</code> object containing
     *         the pre-compiled SQL statement
     * @exception SQLException
     *                if a database access error occurs
     */
    @Override
    public PreparedStatement prepareStatement(String sql) throws SQLException {
        testIfClosed();

        return new PreparedStatementHttp(this, sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
                ResultSet.CLOSE_CURSORS_AT_COMMIT);
    }

    /**
     * 
     * Creates a <code>PreparedStatement</code> object that will generate
     * <code>ResultSet</code> objects with the given type and concurrency. This
     * method is the same as the <code>prepareStatement</code> method above, but
     * it allows the default result set type and concurrency to be overridden.
     * The holdability of the created result sets can be determined by calling
     * {@link #getHoldability}.
     * 
     * @param sql
     *            a <code>String</code> object that is the SQL statement to be
     *            sent to the database; may contain one or more '?' IN
     *            parameters
     * @param resultSetType
     *            a result set type; one of
     *            <code>ResultSet.TYPE_FORWARD_ONLY</code>,
     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or
     *            <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
     * @param resultSetConcurrency
     *            a concurrency type; one of
     *            <code>ResultSet.CONCUR_READ_ONLY</code> or
     *            <code>ResultSet.CONCUR_UPDATABLE</code>
     * @return a new PreparedStatement object containing the pre-compiled SQL
     *         statement that will produce <code>ResultSet</code> objects with
     *         the given type and concurrency
     * @exception SQLException
     *                if a database access error occurs, this method is called
     *                on a closed connection or the given parameters are not
     *                <code>ResultSet</code> constants indicating type and
     *                concurrency
     * @exception SQLFeatureNotSupportedException
     *                if the JDBC driver does not support this method or this
     *                method is not supported for the specified result set type
     *                and result set concurrency.
     * @since 1.2
     */
    @Override
    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
            throws SQLException {
        testIfClosed();

        // We support only ResultSet.CONCUR_READ_ONLY
        if (resultSetConcurrency == ResultSet.CONCUR_UPDATABLE) {
            throw new SQLFeatureNotSupportedException("Concurrency ResultSet.CONCUR_UPDATABLE is not supported.");
        }

        return new PreparedStatementHttp(this, sql, resultSetType, resultSetConcurrency, getHoldability());

    }

    /**
     * Creates a <code>PreparedStatement</code> object that will generate
     * <code>ResultSet</code> objects with the given type, concurrency, and
     * holdability.
     * <P>
     * This method is the same as the <code>prepareStatement</code> method
     * above, but it allows the default result set type, concurrency, and
     * holdability to be overridden.
     * 
     * @param sql
     *            a <code>String</code> object that is the SQL statement to be
     *            sent to the database; may contain one or more '?' IN
     *            parameters
     * @param resultSetType
     *            one of the following <code>ResultSet</code> constants:
     *            <code>ResultSet.TYPE_FORWARD_ONLY</code>,
     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or
     *            <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
     * @param resultSetConcurrency
     *            one of the following <code>ResultSet</code> constants:
     *            <code>ResultSet.CONCUR_READ_ONLY</code> or
     *            <code>ResultSet.CONCUR_UPDATABLE</code>
     * @param resultSetHoldability
     *            one of the following <code>ResultSet</code> constants:
     *            <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or
     *            <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>
     * @return a new <code>PreparedStatement</code> object, containing the
     *         pre-compiled SQL statement, that will generate
     *         <code>ResultSet</code> objects with the given type, concurrency,
     *         and holdability
     * @exception SQLException
     *                if a database access error occurs, this method is called
     *                on a closed connection or the given parameters are not
     *                <code>ResultSet</code> constants indicating type,
     *                concurrency, and holdability
     * @exception SQLFeatureNotSupportedException
     *                if the JDBC driver does not support this method or this
     *                method is not supported for the specified result set type,
     *                result set holdability and result set concurrency.
     * @see ResultSet
     * @since 1.4
     */
    @Override
    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency,
            int resultSetHoldability) throws SQLException {
        testIfClosed();

        // We support only ResultSet.CONCUR_READ_ONLY
        if (resultSetConcurrency == ResultSet.CONCUR_UPDATABLE) {
            throw new SQLFeatureNotSupportedException("Concurrency ResultSet.CONCUR_UPDATABLE is not supported.");
        }

        // We support only ResultSet.HOLD_CURSORS_OVER_COMMIT
        if (resultSetHoldability == ResultSet.HOLD_CURSORS_OVER_COMMIT) {
            throw new SQLFeatureNotSupportedException(
                    "Concurrency ResultSet.HOLD_CURSORS_OVER_COMMIT is not supported.");
        }

        return new PreparedStatementHttp(this, sql, resultSetType, resultSetConcurrency, resultSetHoldability);
    }

    /**
     * Creates a default <code>PreparedStatement</code> object that has the
     * capability to retrieve auto-generated keys. The given constant tells the
     * driver whether it should make auto-generated keys available for
     * retrieval. This parameter is ignored if the SQL statement is not an
     * <code>INSERT</code> statement, or an SQL statement able to return
     * auto-generated keys (the list of such statements is vendor-specific).
     * <P>
     * <B>Note:</B> This method is optimized for handling parametric SQL
     * statements that benefit from precompilation. If the driver supports
     * precompilation, the method <code>prepareStatement</code> will send the
     * statement to the database for precompilation. Some drivers may not
     * support precompilation. In this case, the statement may not be sent to
     * the database until the <code>PreparedStatement</code> object is executed.
     * This has no direct effect on users; however, it does affect which methods
     * throw certain SQLExceptions.
     * <P>
     * Result sets created using the returned <code>PreparedStatement</code>
     * object will by default be type <code>TYPE_FORWARD_ONLY</code> and have a
     * concurrency level of <code>CONCUR_READ_ONLY</code>. The holdability of
     * the created result sets can be determined by calling
     * {@link #getHoldability}.
     * 
     * @param sql
     *            an SQL statement that may contain one or more '?' IN parameter
     *            placeholders
     * @param autoGeneratedKeys
     *            a flag indicating whether auto-generated keys should be
     *            returned; one of <code>Statement.RETURN_GENERATED_KEYS</code>
     *            or <code>Statement.NO_GENERATED_KEYS</code>
     * @return a new <code>PreparedStatement</code> object, containing the
     *         pre-compiled SQL statement, that will have the capability of
     *         returning auto-generated keys
     * @exception SQLException
     *                if a database access error occurs, this method is called
     *                on a closed connection or the given parameter is not a
     *                <code>Statement</code> constant indicating whether
     *                auto-generated keys should be returned
     * @exception SQLFeatureNotSupportedException
     *                if the JDBC driver does not support this method with a
     *                constant of Statement.RETURN_GENERATED_KEYS
     * @since 1.4
     */
    public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {

        if (autoGeneratedKeys != Statement.RETURN_GENERATED_KEYS
                && autoGeneratedKeys != Statement.NO_GENERATED_KEYS) {
            throw new SQLException(
                    "Invalid parameter autoGeneratedKeys value. Must be 1 or 2. Valus is: " + autoGeneratedKeys);
        }

        return new PreparedStatementHttp(this, sql, autoGeneratedKeys);

    }

    /**
     * Creates a default <code>PreparedStatement</code> object capable of
     * returning the auto-generated keys designated by the given array. This
     * array contains the indexes of the columns in the target table that
     * contain the auto-generated keys that should be made available. The driver
     * will ignore the array if the SQL statement is not an <code>INSERT</code>
     * statement, or an SQL statement able to return auto-generated keys (the
     * list of such statements is vendor-specific).
     * <p>
     * An SQL statement with or without IN parameters can be pre-compiled and
     * stored in a <code>PreparedStatement</code> object. This object can then
     * be used to efficiently execute this statement multiple times.
     * <P>
     * <B>Note:</B> This method is optimized for handling parametric SQL
     * statements that benefit from precompilation. If the driver supports
     * precompilation, the method <code>prepareStatement</code> will send the
     * statement to the database for precompilation. Some drivers may not
     * support precompilation. In this case, the statement may not be sent to
     * the database until the <code>PreparedStatement</code> object is executed.
     * This has no direct effect on users; however, it does affect which methods
     * throw certain SQLExceptions.
     * <P>
     * Result sets created using the returned <code>PreparedStatement</code>
     * object will by default be type <code>TYPE_FORWARD_ONLY</code> and have a
     * concurrency level of <code>CONCUR_READ_ONLY</code>. The holdability of
     * the created result sets can be determined by calling
     * {@link #getHoldability}.
     * 
     * @param sql
     *            an SQL statement that may contain one or more '?' IN parameter
     *            placeholders
     * @param columnIndexes
     *            an array of column indexes indicating the columns that should
     *            be returned from the inserted row or rows
     * @return a new <code>PreparedStatement</code> object, containing the
     *         pre-compiled statement, that is capable of returning the
     *         auto-generated keys designated by the given array of column
     *         indexes
     * @exception SQLException
     *                if a database access error occurs or this method is called
     *                on a closed connection
     * @exception SQLFeatureNotSupportedException
     *                if the JDBC driver does not support this method
     * 
     * @since 1.4
     */
    public PreparedStatement prepareStatement(String sql, int columnIndexes[]) throws SQLException {

        if (columnIndexes == null) {
            throw new SQLException("columnIndexes can not be null");
        }

        return new PreparedStatementHttp(this, sql, columnIndexes);
    }

    /**
     * Creates a default <code>PreparedStatement</code> object capable of
     * returning the auto-generated keys designated by the given array. This
     * array contains the names of the columns in the target table that contain
     * the auto-generated keys that should be returned. The driver will ignore
     * the array if the SQL statement is not an <code>INSERT</code> statement,
     * or an SQL statement able to return auto-generated keys (the list of such
     * statements is vendor-specific).
     * <P>
     * An SQL statement with or without IN parameters can be pre-compiled and
     * stored in a <code>PreparedStatement</code> object. This object can then
     * be used to efficiently execute this statement multiple times.
     * <P>
     * <B>Note:</B> This method is optimized for handling parametric SQL
     * statements that benefit from precompilation. If the driver supports
     * precompilation, the method <code>prepareStatement</code> will send the
     * statement to the database for precompilation. Some drivers may not
     * support precompilation. In this case, the statement may not be sent to
     * the database until the <code>PreparedStatement</code> object is executed.
     * This has no direct effect on users; however, it does affect which methods
     * throw certain SQLExceptions.
     * <P>
     * Result sets created using the returned <code>PreparedStatement</code>
     * object will by default be type <code>TYPE_FORWARD_ONLY</code> and have a
     * concurrency level of <code>CONCUR_READ_ONLY</code>. The holdability of
     * the created result sets can be determined by calling
     * {@link #getHoldability}.
     * 
     * @param sql
     *            an SQL statement that may contain one or more '?' IN parameter
     *            placeholders
     * @param columnNames
     *            an array of column names indicating the columns that should be
     *            returned from the inserted row or rows
     * @return a new <code>PreparedStatement</code> object, containing the
     *         pre-compiled statement, that is capable of returning the
     *         auto-generated keys designated by the given array of column names
     * @exception SQLException
     *                if a database access error occurs or this method is called
     *                on a closed connection
     * @exception SQLFeatureNotSupportedException
     *                if the JDBC driver does not support this method
     * 
     * @since 1.4
     */
    public PreparedStatement prepareStatement(String sql, String columnNames[]) throws SQLException {

        if (columnNames == null) {
            throw new SQLException("columnNames can not be null");
        }

        return new PreparedStatementHttp(this, sql, columnNames);

    }

    /**
     * Constructs an object that implements the <code>Blob</code> interface. The
     * object returned initially contains no data. The
     * <code>setBinaryStream</code> and <code>setBytes</code> methods of the
     * <code>Blob</code> interface may be used to add data to the
     * <code>Blob</code>.
     * 
     * @return An object that implements the <code>Blob</code> interface
     * @throws SQLException
     *             if an object that implements the <code>Blob</code> interface
     *             can not be constructed, this method is called on a closed
     *             connection or a database access error occurs.
     * @exception SQLFeatureNotSupportedException
     *                if the JDBC driver does not support this data type
     * 
     * @since 1.6
     */
    @Override
    public Blob createBlob() throws SQLException {
        testIfClosed();
        BlobHttp blob = new BlobHttp();
        return blob;
    }

    /**
     * Constructs an object that implements the <code>Clob</code> interface. The
     * object returned initially contains no data. The
     * <code>setAsciiStream</code>, <code>setCharacterStream</code> and
     * <code>setString</code> methods of the <code>Clob</code> interface may be
     * used to add data to the <code>Clob</code>.
     * 
     * @return An object that implements the <code>Clob</code> interface
     * @throws SQLException
     *             if an object that implements the <code>Clob</code> interface
     *             can not be constructed, this method is called on a closed
     *             connection or a database access error occurs.
     * @exception SQLFeatureNotSupportedException
     *                if the JDBC driver does not support this data type
     * 
     * @since 1.6
     */
    @Override
    public Clob createClob() throws SQLException {
        testIfClosed();
        ClobHttp clob = new ClobHttp();
        return clob;
    }

    /**
     * Retrieves the current auto-commit mode for this <code>Connection</code>
     * object.
     * 
     * @return the current state of this <code>Connection</code> object's
     *         auto-commit mode
     * @exception SQLException
     *                if a database access error occurs
     * @see #setAutoCommit
     */
    @Override
    public boolean getAutoCommit() throws SQLException {
        testIfClosed();
        return this.autoCommit;
    }

    /**
     * Sets this connection's auto-commit mode to the given state. If a
     * connection is in auto-commit mode, then all its SQL statements will be
     * executed and committed as individual transactions. Otherwise, its SQL
     * statements are grouped into transactions that are terminated by a call to
     * either the method <code>commit</code> or the method <code>rollback</code>
     * . By default, new connections are in auto-commit mode.
     * <P>
     * The commit occurs when the statement completes or the next execute
     * occurs, whichever comes first. In the case of statements returning a
     * <code>ResultSet</code> object, the statement completes when the last row
     * of the <code>ResultSet</code> object has been retrieved or the
     * <code>ResultSet</code> object has been closed. In advanced cases, a
     * single statement may return multiple results as well as output parameter
     * values. In these cases, the commit occurs when all results and output
     * parameter values have been retrieved.
     * <P>
     * <B>NOTE:</B> If this method is called during a transaction, the
     * transaction is committed.
     * 
     * @param autoCommit
     *            <code>true</code> to enable auto-commit mode;
     *            <code>false</code> to disable it
     * @exception SQLException
     *                if a database access error occurs
     * @see #getAutoCommit
     */
    @Override
    public void setAutoCommit(boolean autoCommit) throws SQLException {
        testIfClosed();

        if (statelessMode) {
            if (autoCommit && !this.statementHolderFileList.isEmpty()) {
                try {
                    // Execute all pending statements
                    receiveFromExecuteUpdate = getStringFromExecuteUpdateListOnServer();
                } finally {
                    resetStatementHolderList(); // Safety reset of list
                }
            }
        } else {
            JdbcHttpTransactionTransfer jdbcHttpTransactionTransfer = new JdbcHttpTransactionTransfer(this,
                    authenticationToken);
            jdbcHttpTransactionTransfer.setAutoCommit(autoCommit);
        }

        this.autoCommit = autoCommit;
    }

    /**
     * Makes all changes made since the previous commit/rollback permanent and
     * releases any database locks currently held by this
     * <code>Connection</code> object. This method should be used only when
     * auto-commit mode has been disabled.
     * 
     * @exception SQLException
     *                if a database access error occurs or this
     *                <code>Connection</code> object is in auto-commit mode
     * @see #setAutoCommit
     */
    @Override
    public void commit() throws SQLException {
        testIfClosed();

        if (statelessMode) {
            // Execute all pending statements
            if (!this.statementHolderFileList.isEmpty()) {
                try {
                    receiveFromExecuteUpdate = getStringFromExecuteUpdateListOnServer();
                } finally {
                    resetStatementHolderList(); // Safety reset of list
                }
            }
        } else {
            JdbcHttpTransactionTransfer jdbcHttpTransactionTransfer = new JdbcHttpTransactionTransfer(this,
                    authenticationToken);
            jdbcHttpTransactionTransfer.commit();
        }

    }

    /**
     * Undoes all changes made in the current transaction and releases any
     * database locks currently held by this <code>Connection</code> object.
     * This method should be used only when auto-commit mode has been disabled.
     * 
     * @exception SQLException
     *                if a database access error occurs or this
     *                <code>Connection</code> object is in auto-commit mode
     * @see #setAutoCommit
     */
    @Override
    public void rollback() throws SQLException {
        testIfClosed();

        if (statelessMode) {
            resetStatementHolderList();
        } else {
            JdbcHttpTransactionTransfer jdbcHttpTransactionTransfer = new JdbcHttpTransactionTransfer(this,
                    authenticationToken);
            jdbcHttpTransactionTransfer.rollback();
        }

    }

    /**
     * Creates an unnamed savepoint in the current transaction and returns the
     * new <code>Savepoint</code> object that represents it.
     * 
     * @return the new <code>Savepoint</code> object
     * @exception SQLException
     *                if a database access error occurs or this
     *                <code>Connection</code> object is currently in auto-commit
     *                mode
     * @see Savepoint
     * @since 1.4
     */
    @Override
    public Savepoint setSavepoint() throws SQLException {

        if (statelessMode) {
            throw new SQLException(FEATURE_NOT_SUPPORTED_IN_STATELESS_MODE);
        } else {
            JdbcHttpSavepointTransfer jdbcHttpSavepointTransfer = new JdbcHttpSavepointTransfer(this,
                    authenticationToken);
            Savepoint savepoint = jdbcHttpSavepointTransfer.setSavepoint();
            return savepoint;
        }
    }

    /**
     * Creates a savepoint with the given name in the current transaction and
     * returns the new <code>Savepoint</code> object that represents it.
     * 
     * @param name
     *            a <code>String</code> containing the name of the savepoint
     * @return the new <code>Savepoint</code> object
     * @exception SQLException
     *                if a database access error occurs or this
     *                <code>Connection</code> object is currently in auto-commit
     *                mode
     * @see Savepoint
     * @since 1.4
     */
    @Override
    public Savepoint setSavepoint(String name) throws SQLException {
        if (statelessMode) {
            throw new SQLException(FEATURE_NOT_SUPPORTED_IN_STATELESS_MODE);
        } else {
            JdbcHttpSavepointTransfer jdbcHttpSavepointTransfer = new JdbcHttpSavepointTransfer(this,
                    authenticationToken);
            Savepoint savepoint = jdbcHttpSavepointTransfer.setSavepoint(name);
            return savepoint;
        }
    }

    /**
     * Undoes all changes made after the given <code>Savepoint</code> object was
     * set.
     * <P>
     * This method should be used only when auto-commit has been disabled.
     * 
     * @param savepoint
     *            the <code>Savepoint</code> object to roll back to
     * @exception SQLException
     *                if a database access error occurs, the
     *                <code>Savepoint</code> object is no longer valid, or this
     *                <code>Connection</code> object is currently in auto-commit
     *                mode
     * @see Savepoint
     * @see #rollback
     * @since 1.4
     */
    @Override
    public void rollback(Savepoint savepoint) throws SQLException {
        if (statelessMode) {
            throw new SQLException(FEATURE_NOT_SUPPORTED_IN_STATELESS_MODE);
        } else {
            JdbcHttpSavepointTransfer jdbcHttpSavepointTransfer = new JdbcHttpSavepointTransfer(this,
                    authenticationToken);
            jdbcHttpSavepointTransfer.rollback(savepoint);
            return;
        }

    }

    /**
     * Undoes all changes made after the given <code>Savepoint</code> object was
     * set.
     * <P>
     * This method should be used only when auto-commit has been disabled.
     * 
     * @param savepoint
     *            the <code>Savepoint</code> object to roll back to
     * @exception SQLException
     *                if a database access error occurs, this method is called
     *                while participating in a distributed transaction, this
     *                method is called on a closed connection, the
     *                <code>Savepoint</code> object is no longer valid, or this
     *                <code>Connection</code> object is currently in auto-commit
     *                mode
     * @exception SQLFeatureNotSupportedException
     *                if the JDBC driver does not support this method
     * @see Savepoint
     * @see #rollback
     * @since 1.4
     */
    @Override
    public void releaseSavepoint(Savepoint savepoint) throws SQLException {
        if (statelessMode) {
            throw new SQLException(FEATURE_NOT_SUPPORTED_IN_STATELESS_MODE);
        } else {
            JdbcHttpSavepointTransfer jdbcHttpSavepointTransfer = new JdbcHttpSavepointTransfer(this,
                    authenticationToken);
            jdbcHttpSavepointTransfer.releaseSavepoint(savepoint);
            return;
        }
    }

    /**
     * Releases this <code>Connection</code> object's database and JDBC
     * resources immediately instead of waiting for them to be automatically
     * released.
     * <P>
     * Calling the method <code>close</code> on a <code>Connection</code> object
     * that is already closed is a no-op.
     * <P>
     * <B>Note:</B> A <code>Connection</code> object is automatically closed
     * when it is garbage collected. Certain fatal errors also close a
     * <code>Connection</code> object.
     * 
     * @exception SQLException
     *                if a database access error occurs
     */
    @Override
    public void close() throws SQLException {

        if (this.isClosed) {
            return;
        }

        debug("Before jdbcHttpTransactionTransfer.close()");

        if (!statelessMode) {
            final ConnectionHttp theConnection = this;
            try {
                JdbcHttpTransactionTransfer jdbcHttpTransactionTransfer = new JdbcHttpTransactionTransfer(
                        theConnection, authenticationToken);
                jdbcHttpTransactionTransfer.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        debug("After jdbcHttpTransactionTransfer.close()");

        this.username = null;
        this.authenticationToken = null;

        this.sessionParameters = null;
        this.authenticationToken = null;
        this.statementHolderFileList = null;

        if (httpTransfer != null) {
            httpTransfer.close();
            httpTransfer = null;
        }

        if (remoteSession != null) {
            remoteSession.logoff();
            remoteSession = null;
        }

        debug("databaseMetaDataMap: " + databaseMetaDataMap);
        databaseMetaDataMap.remove(this);
        debug("databaseMetaDataMap: " + databaseMetaDataMap);

        this.isClosed = true;
    }

    /**
     * Retrieves whether this <code>Connection</code> object has been closed. A
     * connection is closed if the method <code>close</code> has been called on
     * it or if certain fatal errors have occurred. This method is guaranteed to
     * return <code>true</code> only when it is called after the method
     * <code>Connection.close</code> has been called.
     * <P>
     * This method generally cannot be called to determine whether a connection
     * to a database is valid or invalid. A typical client can determine that a
     * connection is invalid by catching any exceptions that might be thrown
     * when an operation is attempted.
     * 
     * @return <code>true</code> if this <code>Connection</code> object is
     *         closed; <code>false</code> if it is still open
     * @exception SQLException
     *                if a database access error occurs
     */
    @Override
    public boolean isClosed() throws SQLException {
        return this.isClosed;
    }

    /**
     * Puts this connection in read-only mode as a hint to the driver to enable
     * database optimizations.
     * 
     * <P>
     * <B>Note:</B> This method cannot be called during a transaction.
     * 
     * @param readOnly
     *            <code>true</code> enables read-only mode; <code>false</code>
     *            disables it
     * @exception SQLException
     *                if a database access error occurs or this method is called
     *                during a transaction
     */
    @Override
    public void setReadOnly(boolean readOnly) throws SQLException {
        testIfClosed();

        if (!statelessMode) {
            JdbcHttpTransactionTransfer jdbcHttpTransactionTransfer = new JdbcHttpTransactionTransfer(this,
                    authenticationToken);
            jdbcHttpTransactionTransfer.setReadOnly(readOnly);
        }

        this.readOnly = readOnly;
    }

    /**
     * Retrieves whether this <code>Connection</code> object is in read-only
     * mode.
     * 
     * @return <code>true</code> if this <code>Connection</code> object is
     *         read-only; <code>false</code> otherwise
     * @exception SQLException
     *                if a database access error occurs
     */
    @Override
    public boolean isReadOnly() throws SQLException {
        testIfClosed();

        if (!statelessMode) {
            JdbcHttpTransactionTransfer jdbcHttpTransactionTransfer = new JdbcHttpTransactionTransfer(this,
                    authenticationToken);
            this.readOnly = jdbcHttpTransactionTransfer.isReadOnly();
        }

        return this.readOnly;
    }

    /**
     * Retrieves a <code>DatabaseMetaData</code> object that contains metadata
     * about the database to which this <code>Connection</code> object
     * represents a connection. The metadata includes information about the
     * database's tables, its supported SQL grammar, its stored procedures, the
     * capabilities of this connection, and so on.
     * 
     * @return a <code>DatabaseMetaData</code> object for this
     *         <code>Connection</code> object
     * @exception SQLException
     *                if a database access error occurs
     */
    @Override
    public DatabaseMetaData getMetaData() throws SQLException {
        testIfClosed();

        DatabaseMetaData databaseMetaDataLocal = databaseMetaDataMap.get(this);

        if (databaseMetaDataLocal != null) {
            debug("getMetaData(): return DatabaseMetaData from cache.");
            return databaseMetaDataLocal;
        }

        debug("getMetaData(): contacting server. ");

        // Retrieve the JSON formated DatabaseMetaDataHolder from host
        JdbcHttpMetaDataTransfer jdbcHttpMetaDataTransfer = new JdbcHttpMetaDataTransfer(this,
                this.getAuthenticationToken());

        File file = jdbcHttpMetaDataTransfer.getFileFromCallMetaDataFunction("getMetaData");
        String databaseMetaDataHolderString = null;
        try {
            databaseMetaDataHolderString = FileUtils.readFileToString(file);
            databaseMetaDataHolderString = HtmlConverter.fromHtml(databaseMetaDataHolderString);
        } catch (IOException e) {
            JdbcHttpTransferUtil.wrapExceptionAsSQLException(e);
        }

        if (!DEBUG && !KeepTempFilePolicyParms.KEEP_TEMP_FILE) {
            file.delete();
        }

        // Format the DatabaseMetaDataHolder from JSON String
        DatabaseMetaDataHolder databaseMetaDataHolder = DatabaseMetaDataHolderTransport
                .fromJson(databaseMetaDataHolderString);

        // Build the final DatabaseMetaDataHttp instance
        DatabaseMetaDataHttp databaseMetaData = new DatabaseMetaDataHttp(this, databaseMetaDataHolder);

        databaseMetaDataMap.put(this, databaseMetaData);
        return databaseMetaData;
    }

    /**
     * Sets the given catalog name in order to select a subspace of this
     * <code>Connection</code> object's database in which to work.
     * <P>
     * If the driver does not support catalogs, it will silently ignore this
     * request.
     * 
     * @param catalog
     *            the name of a catalog (subspace in this
     *            <code>Connection</code> object's database) in which to work
     * @exception SQLException
     *                if a database access error occurs
     * @see #getCatalog
     */
    @Override
    public void setCatalog(String catalog) throws SQLException {
        // Do Nothing ==> Ignore silently
    }

    /**
     * Retrieves this <code>Connection</code> object's current catalog name.
     * 
     * @return the current catalog name or <code>null</code> if there is none
     * @exception SQLException
     *                if a database access error occurs
     * @see #setCatalog
     */
    @Override
    public String getCatalog() throws SQLException {
        // Retrieve the JSON formated DatabaseMetaDataHolder from host

        JdbcHttpMetaDataTransfer jdbcHttpMetaDataTransfer = new JdbcHttpMetaDataTransfer(this,
                this.getAuthenticationToken());
        File file = jdbcHttpMetaDataTransfer.getFileFromCallMetaDataFunction("getCatalog");

        String catalog = null;
        try {
            catalog = FileUtils.readFileToString(file);

            if (catalog != null) {
                catalog = catalog.trim();
            }

            catalog = HtmlConverter.fromHtml(catalog);
        } catch (IOException e) {
            JdbcHttpTransferUtil.wrapExceptionAsSQLException(e);
        }

        if (catalog.equals("null")) {
            catalog = null;
        }

        file.delete();
        return catalog;
    }

    /**
     * Attempts to change the transaction isolation level for this
     * <code>Connection</code> object to the one given. The constants defined in
     * the interface <code>Connection</code> are the possible transaction
     * isolation levels.
     * <P>
     * <B>Note:</B> If this method is called during a transaction, the result is
     * implementation-defined.
     * 
     * @param level
     *            one of the following <code>Connection</code> constants:
     *            <code>Connection.TRANSACTION_READ_UNCOMMITTED</code>,
     *            <code>Connection.TRANSACTION_READ_COMMITTED</code>,
     *            <code>Connection.TRANSACTION_REPEATABLE_READ</code>, or
     *            <code>Connection.TRANSACTION_SERIALIZABLE</code>. (Note that
     *            <code>Connection.TRANSACTION_NONE</code> cannot be used
     *            because it specifies that transactions are not supported.)
     * @exception SQLException
     *                if a database access error occurs, this method is called
     *                on a closed connection or the given parameter is not one
     *                of the <code>Connection</code> constants
     * @see DatabaseMetaData#supportsTransactionIsolationLevel
     * @see #getTransactionIsolation
     */
    @Override
    public void setTransactionIsolation(int level) throws SQLException {
        testIfClosed();

        if (level != Connection.TRANSACTION_READ_UNCOMMITTED && level != Connection.TRANSACTION_READ_COMMITTED
                && level != Connection.TRANSACTION_REPEATABLE_READ
                && level != Connection.TRANSACTION_SERIALIZABLE) {
            throw new SQLException("Illegal transaction isolation level: " + level);
        }

        if (!statelessMode) {
            JdbcHttpTransactionTransfer jdbcHttpTransactionTransfer = new JdbcHttpTransactionTransfer(this,
                    authenticationToken);
            jdbcHttpTransactionTransfer.setTransactionIsolation(level);
        }

        this.transactionIsolation = level;
    }

    /**
     * Retrieves the first warning reported by calls on this
     * <code>Connection</code> object. If there is more than one warning,
     * subsequent warnings will be chained to the first one and can be retrieved
     * by calling the method <code>SQLWarning.getNextWarning</code> on the
     * warning that was retrieved previously.
     * <P>
     * This method may not be called on a closed connection; doing so will cause
     * an <code>SQLException</code> to be thrown.
     * 
     * <P>
     * <B>Note:</B> Subsequent warnings will be chained to this SQLWarning.
     * 
     * @return the first <code>SQLWarning</code> object or <code>null</code> if
     *         there are none
     * @exception SQLException
     *                if a database access error occurs or this method is called
     *                on a closed connection
     * @see SQLWarning
     */
    @Override
    public SQLWarning getWarnings() throws SQLException {
        return null;
    }

    /**
     * Clears all warnings reported for this <code>Connection</code> object.
     * After a call to this method, the method <code>getWarnings</code> returns
     * <code>null</code> until a new warning is reported for this
     * <code>Connection</code> object.
     * 
     * @exception SQLException
     *                if a database access error occurs
     */
    @Override
    public void clearWarnings() throws SQLException {
        // Does nothing
    }

    /**
     * Retrieves this <code>Connection</code> object's current transaction
     * isolation level.
     * 
     * @return the current transaction isolation level, which will be one of the
     *         following constants:
     *         <code>Connection.TRANSACTION_READ_UNCOMMITTED</code>,
     *         <code>Connection.TRANSACTION_READ_COMMITTED</code>,
     *         <code>Connection.TRANSACTION_REPEATABLE_READ</code>,
     *         <code>Connection.TRANSACTION_SERIALIZABLE</code>, or
     *         <code>Connection.TRANSACTION_NONE</code>.
     * @exception SQLException
     *                if a database access error occurs or this method is called
     *                on a closed connection
     * @see #setTransactionIsolation
     */
    @Override
    public int getTransactionIsolation() throws SQLException {
        testIfClosed();

        // Ok, now get the default isolation level
        JdbcHttpTransactionTransfer jdbcHttpTransactionTransfer = new JdbcHttpTransactionTransfer(this,
                authenticationToken);
        this.transactionIsolation = jdbcHttpTransactionTransfer.getTransactionIsolation();

        return this.transactionIsolation;
    }

    // --------------------------JDBC 3.0-----------------------------

    /**
     * Changes the default holdability of <code>ResultSet</code> objects created
     * using this <code>Connection</code> object to the given holdability. The
     * default holdability of <code>ResultSet</code> objects can be be
     * determined by invoking {@link DatabaseMetaData#getResultSetHoldability}.
     * 
     * @param holdability
     *            a <code>ResultSet</code> holdability constant; one of
     *            <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or
     *            <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>
     * @throws SQLException
     *             if a database access occurs, this method is called on a
     *             closed connection, or the given parameter is not a
     *             <code>ResultSet</code> constant indicating holdability
     * @exception SQLFeatureNotSupportedException
     *                if the given holdability is not supported
     * @see #getHoldability
     * @see DatabaseMetaData#getResultSetHoldability
     * @see ResultSet
     * @since 1.4
     */
    @Override
    public void setHoldability(int holdability) throws SQLException {
        testIfClosed();

        if (holdability != ResultSet.HOLD_CURSORS_OVER_COMMIT && holdability != ResultSet.CLOSE_CURSORS_AT_COMMIT) {
            throw new SQLException("Illegal holdability: " + holdability);
        }

        if (!statelessMode) {
            JdbcHttpTransactionTransfer jdbcHttpTransactionTransfer = new JdbcHttpTransactionTransfer(this,
                    authenticationToken);
            jdbcHttpTransactionTransfer.setHoldability(holdability);
        } else {
            if (holdability == ResultSet.HOLD_CURSORS_OVER_COMMIT) {
                throw new SQLFeatureNotSupportedException(
                        "holdability ResultSet.HOLD_CURSORS_OVER_COMMIT is not supported");
            }
        }

        this.holdability = holdability;
    }

    /**
     * Retrieves the current holdability of <code>ResultSet</code> objects
     * created using this <code>Connection</code> object.
     * 
     * @return the holdability, one of
     *         <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 connection
     * @see #setHoldability
     * @see DatabaseMetaData#getResultSetHoldability
     * @see ResultSet
     * @since 1.4
     */
    @Override
    public int getHoldability() throws SQLException {
        testIfClosed();

        if (!statelessMode) {
            JdbcHttpTransactionTransfer jdbcHttpTransactionTransfer = new JdbcHttpTransactionTransfer(this,
                    authenticationToken);
            this.holdability = jdbcHttpTransactionTransfer.getHoldability();
        }

        return holdability;
    }

    /**
     * Returns true if the connection has not been closed and is still valid.
     * The driver shall submit a query on the connection or use some other
     * mechanism that positively verifies the connection is still valid when
     * this method is called.
     * <p>
     * The query submitted by the driver to validate the connection shall be
     * executed in the context of the current transaction.
     * 
     * @param timeout
     *            - The time in seconds to wait for the database operation used
     *            to validate the connection to complete. If the timeout period
     *            expires before the operation completes, this method returns
     *            false. A value of 0 indicates a timeout is not applied to the
     *            database operation.
     *            <p>
     * @return true if the connection is valid, false otherwise
     * @exception SQLException
     *                if the value supplied for <code>timeout</code> is less
     *                then 0
     * @since 1.6
     *        <p>
     * @see java.sql.DatabaseMetaData#getClientInfoProperties
     */
    @Override
    public boolean isValid(int timeout) throws SQLException {
        if (statelessMode) {
            throw new SQLException(FEATURE_NOT_SUPPORTED_IN_STATELESS_MODE);

        } else {
            JdbcHttpConnectionInfoTransfer jdbcHttpConnectionInfoTransfer = new JdbcHttpConnectionInfoTransfer(this,
                    authenticationToken);
            boolean isValid = jdbcHttpConnectionInfoTransfer.isValid(timeout);
            return isValid;
        }

    }

    /**
     * Sets the value of the client info property specified by name to the value
     * specified by value.
     * <p>
     * Applications may use the
     * <code>DatabaseMetaData.getClientInfoProperties</code> method to determine
     * the client info properties supported by the driver and the maximum length
     * that may be specified for each property.
     * <p>
     * The driver stores the value specified in a suitable location in the
     * database. For example in a special register, session parameter, or system
     * table column. For efficiency the driver may defer setting the value in
     * the database until the next time a statement is executed or prepared.
     * Other than storing the client information in the appropriate place in the
     * database, these methods shall not alter the behavior of the connection in
     * anyway. The values supplied to these methods are used for accounting,
     * diagnostics and debugging purposes only.
     * <p>
     * The driver shall generate a warning if the client info name specified is
     * not recognized by the driver.
     * <p>
     * If the value specified to this method is greater than the maximum length
     * for the property the driver may either truncate the value and generate a
     * warning or generate a <code>SQLClientInfoException</code>. If the driver
     * generates a <code>SQLClientInfoException</code>, the value specified was
     * not set on the connection.
     * <p>
     * The following are standard client info properties. Drivers are not
     * required to support these properties however if the driver supports a
     * client info property that can be described by one of the standard
     * properties, the standard property name should be used.
     * <p>
     * <ul>
     * <li>ApplicationName - The name of the application currently utilizing the
     * connection</li>
     * <li>ClientUser - The name of the user that the application using the
     * connection is performing work for. This may not be the same as the user
     * name that was used in establishing the connection.</li>
     * <li>ClientHostname - The hostname of the computer the application using
     * the connection is running on.</li>
     * </ul>
     * <p>
     * 
     * @param name
     *            The name of the client info property to set
     * @param value
     *            The value to set the client info property to. If the value is
     *            null, the current value of the specified property is cleared.
     *            <p>
     * @throws SQLClientInfoException
     *             if the database server returns an error while setting the
     *             client info value on the database server or this method is
     *             called on a closed connection
     *             <p>
     * @since 1.6
     */
    @Override
    public void setClientInfo(String name, String value) throws SQLClientInfoException {

        JdbcHttpConnectionInfoTransfer jdbcHttpConnectionInfoTransfer = new JdbcHttpConnectionInfoTransfer(this,
                authenticationToken);
        jdbcHttpConnectionInfoTransfer.setClientInfo(name, value);

    }

    /**
     * Sets the value of the connection's client info properties. The
     * <code>Properties</code> object contains the names and values of the
     * client info properties to be set. The set of client info properties
     * contained in the properties list replaces the current set of client info
     * properties on the connection. If a property that is currently set on the
     * connection is not present in the properties list, that property is
     * cleared. Specifying an empty properties list will clear all of the
     * properties on the connection. See
     * <code>setClientInfo (String, String)</code> for more information.
     * <p>
     * If an error occurs in setting any of the client info properties, a
     * <code>SQLClientInfoException</code> is thrown. The
     * <code>SQLClientInfoException</code> contains information indicating which
     * client info properties were not set. The state of the client information
     * is unknown because some databases do not allow multiple client info
     * properties to be set atomically. For those databases, one or more
     * properties may have been set before the error occurred.
     * <p>
     * 
     * @param properties
     *            the list of client info properties to set
     *            <p>
     * @see java.sql.Connection#setClientInfo(String, String)
     *      setClientInfo(String, String)
     * @since 1.6
     *        <p>
     * @throws SQLClientInfoException
     *             if the database server returns an error while setting the
     *             clientInfo values on the database server or this method is
     *             called on a closed connection
     *             <p>
     */
    @Override
    public void setClientInfo(Properties properties) throws SQLClientInfoException {
        JdbcHttpConnectionInfoTransfer jdbcHttpConnectionInfoTransfer = new JdbcHttpConnectionInfoTransfer(this,
                authenticationToken);
        jdbcHttpConnectionInfoTransfer.setClientInfo(properties);
    }

    /**
     * Returns the value of the client info property specified by name. This
     * method may return null if the specified client info property has not been
     * set and does not have a default value. This method will also return null
     * if the specified client info property name is not supported by the
     * driver.
     * <p>
     * Applications may use the
     * <code>DatabaseMetaData.getClientInfoProperties</code> method to determine
     * the client info properties supported by the driver.
     * <p>
     * 
     * @param name
     *            The name of the client info property to retrieve
     *            <p>
     * @return The value of the client info property specified
     *         <p>
     * @throws SQLException
     *             if the database server returns an error when fetching the
     *             client info value from the database or this method is called
     *             on a closed connection
     *             <p>
     * @since 1.6
     *        <p>
     * @see java.sql.DatabaseMetaData#getClientInfoProperties
     */
    @Override
    public String getClientInfo(String name) throws SQLException {

        JdbcHttpConnectionInfoTransfer jdbcHttpConnectionInfoTransfer = new JdbcHttpConnectionInfoTransfer(this,
                authenticationToken);
        String clientInfo = jdbcHttpConnectionInfoTransfer.getClientInfo(name);
        return clientInfo;

    }

    /**
     * Returns a list containing the name and current value of each client info
     * property supported by the driver. The value of a client info property may
     * be null if the property has not been set and does not have a default
     * value.
     * <p>
     * 
     * @return A <code>Properties</code> object that contains the name and
     *         current value of each of the client info properties supported by
     *         the driver.
     *         <p>
     * @throws SQLException
     *             if the database server returns an error when fetching the
     *             client info values from the database or this method is called
     *             on a closed connection
     *             <p>
     * @since 1.6
     */
    @Override
    public Properties getClientInfo() throws SQLException {
        JdbcHttpConnectionInfoTransfer jdbcHttpConnectionInfoTransfer = new JdbcHttpConnectionInfoTransfer(this,
                authenticationToken);
        Properties clientInfo = jdbcHttpConnectionInfoTransfer.getClientInfo();
        return clientInfo;
    }

    @Override
    public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
        if (statelessMode) {
            throw new SQLException(FEATURE_NOT_SUPPORTED_IN_STATELESS_MODE);

        } else {
            JdbcHttpConnectionInfoTransfer jdbcHttpConnectionInfoTransfer = new JdbcHttpConnectionInfoTransfer(this,
                    authenticationToken);
            Array array = jdbcHttpConnectionInfoTransfer.createArrayOf(typeName, elements);
            return array;
        }
    }

    // Java 1.7 only Methods - No Overloading because we must stay compatible
    // with Java 6

    /**
     * Sets the given schema name to access.
     * <P>
     * If the driver does not support schemas, it will silently ignore this
     * request.
     * <p>
     * Calling {@code setSchema} has no effect on previously created or prepared
     * {@code Statement} objects. It is implementation defined whether a DBMS
     * prepare operation takes place immediately when the {@code Connection}
     * method {@code prepareStatement} or {@code prepareCall} is invoked. For
     * maximum portability, {@code setSchema} should be called before a
     * {@code Statement} is created or prepared.
     * 
     * @param schema
     *            the name of a schema in which to work
     * @exception SQLException
     *                if a database access error occurs or this method is called
     *                on a closed connection
     * @see #getSchema
     * @since 1.7
     */
    @Override
    public void setSchema(String schema) throws SQLException {
        JdbcHttpConnectionInfoTransfer jdbcHttpConnectionInfoTransfer = new JdbcHttpConnectionInfoTransfer(this,
                authenticationToken);
        jdbcHttpConnectionInfoTransfer.setSchema(schema);
    }

    /**
     * Retrieves this <code>Connection</code> object's current schema name.
     * 
     * @return the current schema name or <code>null</code> if there is none
     * @exception SQLException
     *                if a database access error occurs or this method is called
     *                on a closed connection
     * @see #setSchema
     * @since 1.7
     */
    @Override
    public String getSchema() throws SQLException {
        JdbcHttpConnectionInfoTransfer jdbcHttpConnectionInfoTransfer = new JdbcHttpConnectionInfoTransfer(this,
                authenticationToken);
        String schema = jdbcHttpConnectionInfoTransfer.getSchema();
        return schema;
    }

    /**
     * Retrieves the number of milliseconds the driver will wait for a database
     * request to complete. If the limit is exceeded, a
     * <code>SQLException</code> is thrown.
     * 
     * @return the current timeout limit in milliseconds; zero means there is no
     *         limit
     * @throws SQLException
     *             if a database access error occurs or this method is called on
     *             a closed <code>Connection</code>
     * @exception SQLFeatureNotSupportedException
     *                if the JDBC driver does not support this method
     * @see #setNetworkTimeout
     * @since 1.7
     */
    @Override
    public int getNetworkTimeout() throws SQLException {
        JdbcHttpConnectionInfoTransfer jdbcHttpConnectionInfoTransfer = new JdbcHttpConnectionInfoTransfer(this,
                authenticationToken);
        int networkTimeout = jdbcHttpConnectionInfoTransfer.getNetworkTimeout();
        return networkTimeout;
    }

    //
    // Utility dedicated methods for raw ConnectionHttp class that are not
    // Connection
    //

    /**
     * Returns the current Version.
     * 
     * @return the current Version
     */
    public String getVersion() {
        return Version.getVersion();
    }

    /**
     * Returns the URL of the path to the ServerSqlManager Servlet.
     * 
     * @return the URL of the path to the ServerSqlManager Servlet
     */
    public String getUrl() {
        return url;
    }

    /**
     * Returns the username in use.
     * 
     * @return the username in use
     */
    public String getUsername() {
        return this.username;
    }

    /**
     * Returns the Authentication Token
     * 
     * @return the Authentication Token
     */
    String getAuthenticationToken() {
        return authenticationToken;
    }

    /**
     * Returns the <code>SessionParameters</code> instance in use for the
     * current session.
     * 
     * @return the <code>SessionParameters</code> instance in use for the
     *         current session
     */
    public SessionParameters getSessionParameters() {
        return this.sessionParameters;
    }

    /**
     * Returns the {@code Proxy} instance in use for this File Session.
     * 
     * @return the {@code Proxy} instance in use for this File Session
     */
    public Proxy getProxy() {
        return this.proxy;
    }

    /**
     * Returns the proxy credentials
     * 
     * @return the proxy credentials
     */
    public PasswordAuthentication getPasswordAuthentication() {
        return passwordAuthentication;
    }

    /**
     * Allows to get a copy of the current <code>RemoteConnection</code>: use it
     * to do some simultaneous operations in a different thread (in order to
     * avoid conflicts).
     */
    @Override
    public Connection clone() {
        Connection connectionHttp = new ConnectionHttp(this.url, this.username, this.proxy,
                this.passwordAuthentication, this.sessionParameters, remoteSession, statelessMode,
                joinResultSetMetaData, zipResultSet);
        return connectionHttp;
    }

    /**
     * Returns the http status code of the last executed JDBC command that
     * called the remote server.
     * 
     * @return the http status code of the last executed JDBC command that
     *         called the remote server. 0 means the status could not be
     *         returned.
     */
    public int getHttpStatusCode() {
        if (remoteSession != null) {
            return remoteSession.getHttpStatusCode();
        } else {
            return 0;
        }
    }

    /**
     * Gets the current <code>RemoteSession</code> instance (used for file
     * transfers of Clobs and Blobs).
     * <p>
     * 
     * @return the remoteSession instance (to be used for file transfers, per
     *         example)
     */
    public RemoteSession getRemoteSession() {
        return remoteSession;
    }

    /**
     * Returns true if the statement are to be encrypted.
     * 
     * @return true if the statement are to be encrypted. (Default to false).
     */
    public boolean isEncryptStatementParameters() {
        return this.encryptStatementParameters;
    }

    /**
     * Set if the statement parameters are to be encrypted
     * 
     * @param encryptStatementParameters
     *            true if the statement parameters are to be encrypted, else
     *            false
     */
    public void setEncryptStatementParameters(boolean encryptStatementParameters) {
        this.encryptStatementParameters = encryptStatementParameters;
    }

    /**
     * Returns the maximum Number Of Statements that may be transported in
     * memory. If maximum is reached, transport is done using a file.
     * 
     * @return the maximum Number Of Statements that may be transported in
     *         memory.
     */
    public int getMaxStatementsForMemoryTransport() {
        return this.maxStatementsForMemoryTransport;
    }

    /**
     * Sets the maximum Number Of Statements that may be transported in memory
     * from client to server. if maximum is reached, transport is done using a
     * file.
     * 
     * @param maxStatementsForMemoryTransport
     *            the maximum Number Of Statements that may be transported in
     *            memory.
     * 
     */
    public void setMaxStatementsForMemoryTransport(int maxStatementsForMemoryTransport) {
        this.maxStatementsForMemoryTransport = maxStatementsForMemoryTransport;
    }

    /**
     * Test if a connection is still open
     * 
     * @throws SQLException
     *             it the Connection is closed
     */
    private void testIfClosed() throws SQLException {
        if (isClosed()) {
            throw new SQLException("This RemoteConnection is closed!");
        }
    }

    /**
     * Says if session is in stateless mode when connecting to the server.
     * 
     * @return true if session is in stateless mode when connecting to the
     *         server
     */
    public boolean isStatelessMode() {
        return statelessMode;
    }

    /**
     * @return the connectionId
     */
    public String getConnectionId() {
        return connectionId;
    }

    /**
     * Debug tool
     * 
     * @param s
     */

    private void debug(String s) {
        if (DEBUG) {
            ClientLogger.getLogger().log(Level.WARNING, s);
        }
    }

}

// En