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

Java tutorial

Introduction

Here is the source code for org.kawanfw.sql.jdbc.PreparedStatementHttp.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.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.Array;
import java.sql.BatchUpdateException;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Date;
import java.sql.NClob;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.Ref;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.RowId;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLXML;
import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.Vector;
import java.util.logging.Level;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.kawanfw.commons.util.ClientLogger;
import org.kawanfw.commons.util.FrameworkDebug;
import org.kawanfw.commons.util.FrameworkFileUtil;
import org.kawanfw.commons.util.HtmlConverter;
import org.kawanfw.commons.util.KeepTempFilePolicyParms;
import org.kawanfw.commons.util.Tag;
import org.kawanfw.file.api.util.client.FilesTransferWithProgress;
import org.kawanfw.sql.jdbc.http.JdbcHttpBatchTransfer;
import org.kawanfw.sql.jdbc.http.JdbcHttpExecuteRawTransfer;
import org.kawanfw.sql.jdbc.http.JdbcHttpStatementTransfer;
import org.kawanfw.sql.jdbc.http.JdbcHttpTransferUtil;
import org.kawanfw.sql.jdbc.util.ParametersUtil;
import org.kawanfw.sql.jdbc.util.TransportAsciiStream;
import org.kawanfw.sql.jdbc.util.TransportInputStream;
import org.kawanfw.sql.jdbc.util.TransportReader;
import org.kawanfw.sql.json.IntArrayTransport;
import org.kawanfw.sql.json.StatementHolder;
import org.kawanfw.sql.transport.TransportConverter;
import org.kawanfw.sql.util.FileNameFromBlobBuilder;

/**
 * Creates and handle a Prepared Statement Http.
 */

public class PreparedStatementHttp extends StatementHttp implements PreparedStatement {

    /** Debug flag */
    private static boolean DEBUG = FrameworkDebug.isSet(PreparedStatementHttp.class);

    /**
     * The holder that contains the sql order and the list if (parameter type,
     * parameter value ) for prepared statements
     **/
    private StatementHolder statementHolder = null;

    /** The statement to execute */
    private String sql = null;

    /**
     * The update count returned by PreparedStatementHttp.getUpdateCount() after
     * an execute()
     */
    private int updateCount = 0;

    /**
     * The list of local files to uplaod and delete (in fire and forget mode) at
     * end of prepared statement.
     */
    protected List<File> localFilesStatement = null;

    /**
     * The list of remote uploaded files delete (in fire and forget mode) at end
     * of prepared statement.
     */
    protected List<String> remoteFilesStatement = null;

    // /**
    // * The list of input streams to delete (in fire and forget mode) at end of
    // * prepared statement
    // */
    // protected List<InputStream> localInputStreams = null;
    //
    //
    // /** The length of each InputStream */
    // protected List<Long> localInputStreamLengths = null;

    /**
     * Constructor
     * 
     * @param connectionHttp
     *            The Http Connection
     * @param sql
     *            the sql statement to use
     * @param resultSetType
     *            The result set type
     * @param resultSetConcurrency
     *            The result set concurrency
     * @param resultSetHoldability
     *            The result set holdability
     * @throws SQLException
     */
    public PreparedStatementHttp(ConnectionHttp connectionHttp, String sql, int resultSetType,
            int resultSetConcurrency, int resultSetHoldability) throws SQLException {

        super(connectionHttp, resultSetType, resultSetConcurrency, resultSetHoldability);

        if (sql == null) {
            throw new SQLException("sql order is null!");
        }

        this.sql = sql.trim();

        statementHolder = new StatementHolder(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
        statementHolder.setPreparedStatement(true);
        statementHolder.setHtmlEncodingOn(connectionHttp.isHtmlEncodingOn());

        localFilesStatement = new Vector<File>();
        remoteFilesStatement = new Vector<String>();
        // localInputStreams = new Vector<InputStream>();
        // localInputStreamLengths = new Vector<Long>();
    }

    /**
     * Constructor for auto-generated keys
     * 
     * @param connectionHttp
     *            The Http Connection
     * @param sql
     *            the sql statement to use
     * @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>
     * 
     * @throws SQLException
     */
    public PreparedStatementHttp(ConnectionHttp connectionHttp, String sql, int autoGeneratedKeys)
            throws SQLException {

        super(connectionHttp, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
                ResultSet.CLOSE_CURSORS_AT_COMMIT);

        if (sql == null) {
            throw new SQLException("sql order is null!");
        }

        this.sql = sql.trim();

        statementHolder = new StatementHolder(sql, autoGeneratedKeys);
        statementHolder.setPreparedStatement(true);
        statementHolder.setHtmlEncodingOn(connectionHttp.isHtmlEncodingOn());

        localFilesStatement = new Vector<File>();
        remoteFilesStatement = new Vector<String>();
        // localInputStreams = new Vector<InputStream>();
        // localInputStreamLengths = new Vector<Long>();

    }

    /**
     * Constructor for auto-generated keys
     * 
     * @param connectionHttp
     *            The Http Connection
     * @param sql
     *            the sql statement to use
     * @param columnIndexes
     *            an array of column indexes indicating the columns that should
     *            be returned from the inserted row or rows
     * 
     * @throws SQLException
     */

    public PreparedStatementHttp(ConnectionHttp connectionHttp, String sql, int[] columnIndexes)
            throws SQLException {
        super(connectionHttp, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
                ResultSet.CLOSE_CURSORS_AT_COMMIT);

        if (sql == null) {
            throw new SQLException("sql order is null!");
        }

        this.sql = sql.trim();

        statementHolder = new StatementHolder(sql, columnIndexes);
        statementHolder.setPreparedStatement(true);
        statementHolder.setHtmlEncodingOn(connectionHttp.isHtmlEncodingOn());

        localFilesStatement = new Vector<File>();
        remoteFilesStatement = new Vector<String>();
    }

    /**
     * Constructor for auto-generated keys
     * 
     * @param connectionHttp
     *            The Http Connection
     * @param sql
     *            the sql statement to use
     * @param columnNames
     *            an array of column names indicating the columns that should be
     *            returned from the inserted row or rows
     * 
     * @throws SQLException
     */

    public PreparedStatementHttp(ConnectionHttp connectionHttp, String sql, String[] columnNames)
            throws SQLException {
        super(connectionHttp, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
                ResultSet.CLOSE_CURSORS_AT_COMMIT);

        if (sql == null) {
            throw new SQLException("sql order is null!");
        }

        this.sql = sql.trim();

        statementHolder = new StatementHolder(sql, columnNames);
        statementHolder.setPreparedStatement(true);
        statementHolder.setHtmlEncodingOn(connectionHttp.isHtmlEncodingOn());

        localFilesStatement = new Vector<File>();
        remoteFilesStatement = new Vector<String>();
    }

    /**
     * @throws SQLException
     * @see java.sql.Statement#close()
     */
    @Override
    public void close() throws SQLException {

        super.close();
        statementHolder = null;

        deleteLocalContainers();

    }

    /**
     * Executes the SQL query in this <code>PreparedStatement</code> object and
     * returns the <code>ResultSet</code> object generated by the query.
     * 
     * @return a <code>ResultSet</code> object that contains the data produced
     *         by the query; never <code>null</code>
     * @exception SQLException
     *                if a database access error occurs or the SQL statement
     *                does not return a <code>ResultSet</code> object
     */
    @Override
    public ResultSet executeQuery() throws SQLException {

        testIfClosed();

        if (connectionHttp.isStatelessMode()) {
            if (!connectionHttp.getAutoCommit()) {
                throw new IllegalStateException(
                        Tag.PRODUCT + "executeQuery() can\'t be executed when auto commit is off.");
            }

        }

        // Check that all parameters values are set
        ParametersUtil.checkParameters(statementHolder);

        statementHolder.setPreparedStatement(true);
        statementHolder.setExecuteUpdate(false);
        statementHolder.setJoinResultSetMetaData(connectionHttp.isJoinResultSetMetaData());
        statementHolder.setZipResultSet(connectionHttp.isZipResultSet());

        statementHolder.setFetchSize(fetchSize);
        statementHolder.setMaxRows(maxRows);
        statementHolder.setQueryTimeout(queryTimeout);
        statementHolder.setEscapeProcessing(escapeProcessingInt);

        // Send unique order to Server to SQL Executor
        JdbcHttpStatementTransfer jdbcHttpStatementTransfer = new JdbcHttpStatementTransfer(connectionHttp,
                connectionHttp.getAuthenticationToken());

        File receiveFile = jdbcHttpStatementTransfer.getFileFromExecuteQueryOnServer(statementHolder);
        debug("getFileFromexecuteOnServer() : " + receiveFile);

        ResultSet rs = new ResultSetHttp(connectionHttp, statementHolder, this, receiveFile);
        return rs;
    }

    /**
     * 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 localFileStatement : localFilesStatement) {
            totalLength += localFileStatement.length();
        }

        // for (Long localLength : localInputStreamLengths) {
        // totalLength += localLength;
        // }

        return totalLength;
    }

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

        try {

            // reinit progress
            connectionHttp.getProgress().set(0);

            long totalLength = getLocalTotalLength();

            // Upload local objects
            FilesTransferWithProgress filesTransferWithProgress = new FilesTransferWithProgress(
                    connectionHttp.getRemoteSession(), connectionHttp.getProgress(), connectionHttp.getCancelled());

            // 1) Upoad files
            if (!localFilesStatement.isEmpty()) {

                List<File> filesUnmodifiableList = Collections.unmodifiableList(localFilesStatement);

                for (int i = filesUnmodifiableList.size() - 1; i > -1; i--) {
                    // Do the upload
                    debug("uploading file: " + filesUnmodifiableList.get(i));
                    filesTransferWithProgress.upload(localFilesStatement.get(i), remoteFilesStatement.get(i),
                            totalLength);
                    FileUtils.deleteQuietly(filesUnmodifiableList.get(i));
                    localFilesStatement.remove(i);
                }

            }

            // // 2) upload InputStreams
            // if (!localInputStreams.isEmpty()) {
            //
            // //debug("connectionHttp.getProgress(): " +
            // connectionHttp.getProgress());
            // //debug("localInputStreams.size()    : " +
            // localInputStreams.size());
            // //debug("localInputStreamLengths     : " +
            // localInputStreamLengths);
            //
            // filesTransferWithProgress.upload(localInputStreams,
            // localInputStreamLengths, remoteFilesStatement,
            // totalLength);
            // }

        } catch (Exception e) {
            JdbcHttpTransferUtil.wrapExceptionAsSQLException(e);
        } finally {
            // NO! We want to repeat the uploads, so stay at 99
            // connectionHttp.getProgress().set(100);
            // deleteLocalContainers();
        }

    }

    /**
     * Delete local files
     */
    private void deleteLocalContainers() {
        if (!KeepTempFilePolicyParms.KEEP_TEMP_FILE && !DEBUG) {
            if (localFilesStatement != null) {
                for (File localFileStatement : localFilesStatement) {
                    localFileStatement.delete();
                }
            }
        }
    }

    /**
     * Executes the SQL statement in this <code>PreparedStatement</code> object,
     * which must be an SQL <code>INSERT</code>, <code>UPDATE</code> or
     * <code>DELETE</code> statement; or an SQL statement that returns nothing,
     * such as a DDL statement.
     * 
     * @return either (1) the row count for <code>INSERT</code>,
     *         <code>UPDATE</code>, or <code>DELETE</code> statements or (2) 0
     *         for SQL statements that return nothing
     * @exception SQLException
     *                if a database access error occurs or the SQL statement
     *                returns a <code>ResultSet</code> object
     */
    @Override
    public synchronized int executeUpdate() throws SQLException {
        testIfClosed();

        lastExecuteIsRaw = false;

        // Check that all parameters values are set
        ParametersUtil.checkParameters(statementHolder);

        statementHolder.setPreparedStatement(true);
        statementHolder.setExecuteUpdate(true);

        int rc = 0;

        // Safety reset of list if we are in statefull mode
        // For method to be idempotent
        if (!connectionHttp.isStatelessMode()) {
            connectionHttp.resetStatementHolderList();
        }

        // Add the statement to the statement list
        connectionHttp.addStatementHolder(statementHolder);

        // Execute for statefull mode
        if (!connectionHttp.isStatelessMode()) {
            uploadBlobParameters();
        }

        // 25/04/14 15:20 - HACK NDP - There were no parenthesis
        if ((connectionHttp.isStatelessMode() && connectionHttp.getAutoCommit())
                || !connectionHttp.isStatelessMode()) {
            try {

                debug("before getStringFromExecuteUpdateListOnServer()");
                // Send order to Server to SQL Executor
                connectionHttp.receiveFromExecuteUpdate = connectionHttp.getStringFromExecuteUpdateListOnServer();
                debug("after getStringFromExecuteUpdateListOnServer()");

                connectionHttp.testIfUploadInterrupted();

                BufferedReader bufferedReader = new BufferedReader(
                        new StringReader(connectionHttp.receiveFromExecuteUpdate));
                String line1 = null;
                try {
                    line1 = bufferedReader.readLine();
                } catch (IOException e1) {
                    throw new SQLException(e1);
                }

                connectionHttp.testIfUploadInterrupted();

                try {
                    rc = Integer.parseInt(line1);
                } catch (NumberFormatException e) {
                    throw new SQLException(Tag.PRODUCT_PRODUCT_FAIL + e.getMessage(),
                            new IOException(e.getMessage(), e));
                }
            } finally {
                connectionHttp.resetStatementHolderList(); // Safety reset of
                // list

            }
        }

        return rc;
    }

    /**
     * Executes the given SQL statement, which may return multiple results. In
     * some (uncommon) situations, a single SQL statement may return multiple
     * result sets and/or update counts. Normally you can ignore this unless you
     * are (1) executing a stored procedure that you know may return multiple
     * results or (2) you are dynamically executing an unknown SQL string.
     * <P>
     * The <code>execute</code> method executes an SQL statement and indicates
     * the form of the first result. You must then use the methods
     * <code>getResultSet</code> or <code>getUpdateCount</code> to retrieve the
     * result, and <code>getMoreResults</code> to move to any subsequent
     * result(s).
     * 
     * @param sql
     *            any SQL statement
     * @return <code>true</code> if the first result is a <code>ResultSet</code>
     *         object; <code>false</code> if it is an update count or there are
     *         no results
     * @exception SQLException
     *                if a database access error occurs
     * @see #getResultSet
     * @see #getUpdateCount
     * @see #getMoreResults
     */
    @Override
    public boolean execute() throws SQLException {

        testIfClosed();

        if (connectionHttp.isStatelessMode()) {
            if (!connectionHttp.getAutoCommit()) {
                throw new IllegalStateException(
                        Tag.PRODUCT + "execute can\'t be executed when auto commit is off.");
            }
        }

        debug("PreparedStatementHttp.execute : " + sql);

        lastExecuteIsRaw = true;

        // Check that all parameters values are set
        ParametersUtil.checkParameters(statementHolder);

        statementHolder.setPreparedStatement(true);
        statementHolder.setExecuteUpdate(false);

        statementHolder.setJoinResultSetMetaData(connectionHttp.isJoinResultSetMetaData());
        statementHolder.setZipResultSet(connectionHttp.isZipResultSet());

        statementHolder.setFetchSize(fetchSize);
        statementHolder.setMaxRows(maxRows);
        statementHolder.setQueryTimeout(queryTimeout);
        statementHolder.setEscapeProcessing(escapeProcessingInt);

        // Reset the fields values
        rsFromExecute = null;
        updateCount = -1;

        // Send order to Server to SQL Executor
        JdbcHttpExecuteRawTransfer jdbcHttpExecuteRawTransfer = new JdbcHttpExecuteRawTransfer(connectionHttp,
                connectionHttp.getAuthenticationToken());
        receiveFileFromExecute = jdbcHttpExecuteRawTransfer.getFileFromExecuteRaw(statementHolder);

        localFilesExecuteResult.add(receiveFileFromExecute);

        debug("getFileFromexecuteOnServer() : " + receiveFileFromExecute);

        boolean fileResultSet = super.isFileResultSet(receiveFileFromExecute);

        try {
            if (DEBUG) {
                String fileContent = FileUtils.readFileToString(receiveFileFromExecute);
                System.out.println(fileContent);
            }
        } catch (IOException e2) {
            throw new SQLException(e2);
        }

        if (fileResultSet) {
            // Transform the Result Set in String back to an Result Set
            // (emulated)
            rsFromExecute = new ResultSetHttp(connectionHttp, statementHolder, this, receiveFileFromExecute);

            return true;
        } else {

            BufferedReader bufferedReader = null;
            String line1 = null;
            try {
                bufferedReader = new BufferedReader(new FileReader(receiveFileFromExecute));
                line1 = bufferedReader.readLine();
            } catch (IOException e1) {
                throw new SQLException(e1);
            } finally {
                IOUtils.closeQuietly(bufferedReader);
            }

            String updateCountStr = StringUtils.substringAfter(line1, "getUpdateCount=");
            try {
                updateCount = Integer.parseInt(updateCountStr);
            } catch (NumberFormatException e) {
                throw new SQLException(Tag.PRODUCT_PRODUCT_FAIL + e.getMessage(),
                        new IOException(e.getMessage(), e));
            }

            return false;
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.kawanfw.sql.jdbc.StatementHttp#getUpdateCount()
     */
    @Override
    public int getUpdateCount() throws SQLException {
        return updateCount;
    }

    /**
     * Moves to this <code>Statement</code> object's next result, returns
     * <code>true</code> if it is a <code>ResultSet</code> object, and
     * implicitly closes any current <code>ResultSet</code> object(s) obtained
     * with the method <code>getResultSet</code>.
     * 
     * <P>
     * There are no more results when the following is true:
     * 
     * <PRE>
     *      <code>(!getMoreResults() && (getUpdateCount() == -1)</code>
     * </PRE>
     * 
     * @return <code>true</code> if the next result is a <code>ResultSet</code>
     *         object; <code>false</code> if it is an update count or there are
     *         no more results
     * @exception SQLException
     *                if a database access error occurs
     * @see #execute
     */
    public boolean getMoreResults() throws SQLException {
        // always return false for now:
        return false;
    }

    /**
     * Sets the designated parameter to SQL <code>NULL</code>.
     * 
     * <P>
     * <B>Note:</B> You must specify the parameter's SQL type.
     * 
     * @param parameterIndex
     *            the first parameter is 1, the second is 2, ...
     * @param sqlType
     *            the SQL type code defined in <code>java.sql.Types</code>
     * @exception SQLException
     *                if a database access error occurs
     */
    public void setNull(int parameterIndex, int sqlType) throws SQLException {
        testIfClosed();
        statementHolder.setNullParameter(parameterIndex, sqlType);
    }

    /**
     * Sets the designated parameter to the given Java <code>boolean</code>
     * value. The driver converts this to an SQL <code>BIT</code> value when it
     * sends it to the database.
     * 
     * @param parameterIndex
     *            the first parameter is 1, the second is 2, ...
     * @param x
     *            the parameter value
     * @exception SQLException
     *                if a database access error occurs
     */
    @Override
    public void setBoolean(int parameterIndex, boolean x) throws SQLException {
        testIfClosed();
        statementHolder.setParameter(parameterIndex, x);
    }

    /**
     * Sets the designated parameter to the given Java <code>short</code> value.
     * The driver converts this to an SQL <code>SMALLINT</code> value when it
     * sends it to the database.
     * 
     * @param parameterIndex
     *            the first parameter is 1, the second is 2, ...
     * @param x
     *            the parameter value
     * @exception SQLException
     *                if a database access error occurs
     */
    @Override
    public void setShort(int parameterIndex, short x) throws SQLException {
        testIfClosed();
        statementHolder.setParameter(parameterIndex, x);
    }

    /**
     * Sets the designated parameter to the given Java <code>int</code> value.
     * The driver converts this to an SQL <code>INTEGER</code> value when it
     * sends it to the database.
     * 
     * @param parameterIndex
     *            the first parameter is 1, the second is 2, ...
     * @param x
     *            the parameter value
     * @exception SQLException
     *                if a database access error occurs
     */
    @Override
    public void setInt(int parameterIndex, int x) throws SQLException {
        testIfClosed();
        statementHolder.setParameter(parameterIndex, x);
    }

    /**
     * Sets the designated parameter to the given Java <code>long</code> value.
     * The driver converts this to an SQL <code>BIGINT</code> value when it
     * sends it to the database.
     * 
     * @param parameterIndex
     *            the first parameter is 1, the second is 2, ...
     * @param x
     *            the parameter value
     * @exception SQLException
     *                if a database access error occurs
     */
    @Override
    public void setLong(int parameterIndex, long x) throws SQLException {
        testIfClosed();
        statementHolder.setParameter(parameterIndex, x);
    }

    /**
     * Sets the designated parameter to the given Java <code>float</code> value.
     * The driver converts this to an SQL <code>FLOAT</code> value when it sends
     * it to the database.
     * 
     * @param parameterIndex
     *            the first parameter is 1, the second is 2, ...
     * @param x
     *            the parameter value
     * @exception SQLException
     *                if a database access error occurs
     */
    @Override
    public void setFloat(int parameterIndex, float x) throws SQLException {
        testIfClosed();
        statementHolder.setParameter(parameterIndex, x);
    }

    /**
     * Sets the designated parameter to the given Java <code>double</code>
     * value. The driver converts this to an SQL <code>DOUBLE</code> value when
     * it sends it to the database.
     * 
     * @param parameterIndex
     *            the first parameter is 1, the second is 2, ...
     * @param x
     *            the parameter value
     * @exception SQLException
     *                if a database access error occurs
     */
    @Override
    public void setDouble(int parameterIndex, double x) throws SQLException {
        testIfClosed();
        statementHolder.setParameter(parameterIndex, x);
    }

    /**
     * Sets the designated parameter to the given
     * <code>java.math.BigDecimal</code> value. The driver converts this to an
     * SQL <code>NUMERIC</code> value when it sends it to the database.
     * 
     * @param parameterIndex
     *            the first parameter is 1, the second is 2, ...
     * @param x
     *            the parameter value
     * @exception SQLException
     *                if parameterIndex does not correspond to a parameter
     *                marker in the SQL statement; if a database access error
     *                occurs or this method is called on a closed
     *                <code>PreparedStatement</code>
     */
    @Override
    public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException {
        testIfClosed();
        statementHolder.setParameter(parameterIndex, x);
    }

    /**
     * Sets the designated parameter to the given Java <code>String</code>
     * value. The driver converts this to an SQL <code>VARCHAR</code> or
     * <code>LONGVARCHAR</code> value (depending on the argument's size relative
     * to the driver's limits on <code>VARCHAR</code> values) when it sends it
     * to the database.
     * 
     * @param parameterIndex
     *            the first parameter is 1, the second is 2, ...
     * @param x
     *            the parameter value
     * @exception SQLException
     *                if a database access error occurs
     */
    @Override
    public void setString(int parameterIndex, String x) throws SQLException {
        testIfClosed();
        // x = TransportConverter.toTransportFormat(x);

        if (x != null && x.length() > connectionHttp.getMaxLengthForString()) {
            throw new SQLException("String is too big for upload: " + x.length()
                    + " bytes. Maximum length authorized is: " + connectionHttp.getMaxLengthForString());
        }

        statementHolder.setParameter(parameterIndex, x);
    }

    /**
     * Sets the designated parameter to the given <code>String</code> object.
     * The driver converts this to a SQL <code>NCHAR</code> or
     * <code>NVARCHAR</code> or <code>LONGNVARCHAR</code> value (depending on
     * the argument's size relative to the driver's limits on
     * <code>NVARCHAR</code> values) when it sends it to the database.
     *
     * @param parameterIndex
     *            of the first parameter is 1, the second is 2, ...
     * @param value
     *            the parameter value
     * @throws SQLException
     *             if parameterIndex does not correspond to a parameter marker
     *             in the SQL statement; if the driver does not support national
     *             character sets; if the driver can detect that a data
     *             conversion error could occur; if a database access error
     *             occurs; or this method is called on a closed
     *             <code>PreparedStatement</code>
     * @throws SQLFeatureNotSupportedException
     *             if the JDBC driver does not support this method
     * @since 1.6
     */
    @Override
    public void setNString(int parameterIndex, String value) throws SQLException {
        testIfClosed();
        // x = TransportConverter.toTransportFormat(x);

        if (value != null && value.length() > connectionHttp.getMaxLengthForString()) {
            throw new SQLException("String is too big for upload: " + value.length()
                    + " bytes. Maximum length authorized is: " + connectionHttp.getMaxLengthForString());
        }

        statementHolder.setParameter(parameterIndex, value, StatementHolder.SET_N_STRING);
    }

    /**
     * Sets the designated parameter to the given Java <code>byte</code> value.
     * The driver converts this to an SQL <code>TINYINT</code> value when it
     * sends it to the database.
     * 
     * @param parameterIndex
     *            the first parameter is 1, the second is 2, ...
     * @param x
     *            the parameter value
     * @exception SQLException
     *                if a database access error occurs
     */
    @Override
    public void setByte(int parameterIndex, byte x) throws SQLException {
        testIfClosed();
        byte[] theByte = new byte[1];
        theByte[0] = x;
        String encodedString = TransportConverter.toTransportFormat(theByte);
        // parameterValues.put(parameterIndex, hexString);
        statementHolder.setParameter(parameterIndex, encodedString);
    }

    /**
     * Sets the designated parameter to the given Java array of bytes. The
     * driver converts this to an SQL <code>VARBINARY</code> or
     * <code>LONGVARBINARY</code> (depending on the argument's size relative to
     * the driver's limits on <code>VARBINARY</code> values) when it sends it to
     * the database.
     * 
     * @param parameterIndex
     *            the first parameter is 1, the second is 2, ...
     * @param x
     *            the parameter value
     * @exception SQLException
     *                if a database access error occurs
     */
    @Override
    public void setBytes(int parameterIndex, byte[] x) throws SQLException {
        testIfClosed();
        String encodedString = TransportConverter.toTransportFormat(x);
        // parameterValues.put(parameterIndex, hexString);
        statementHolder.setParameter(parameterIndex, encodedString);
    }

    /**
     * Sets the designated parameter to the given <code>java.sql.Date</code>
     * value. The driver converts this to an SQL <code>DATE</code> value when it
     * sends it to the database.
     * 
     * @param parameterIndex
     *            the first parameter is 1, the second is 2, ...
     * @param x
     *            the parameter value
     * @exception SQLException
     *                if a database access error occurs
     */
    @Override
    public void setDate(int parameterIndex, java.sql.Date x) throws SQLException {
        testIfClosed();
        statementHolder.setParameter(parameterIndex, x);
    }

    /**
     * Sets the designated parameter to the given <code>java.sql.Time</code>
     * value. The driver converts this to an SQL <code>TIME</code> value when it
     * sends it to the database.
     * 
     * @param parameterIndex
     *            the first parameter is 1, the second is 2, ...
     * @param x
     *            the parameter value
     * @exception SQLException
     *                if a database access error occurs
     */
    public void setTime(int parameterIndex, java.sql.Time x) throws SQLException {
        testIfClosed();
        statementHolder.setParameter(parameterIndex, x);
    }

    /**
     * Sets the designated parameter to the given
     * <code>java.sql.Timestamp</code> value. The driver converts this to an SQL
     * <code>TIMESTAMP</code> value when it sends it to the database.
     * 
     * @param parameterIndex
     *            the first parameter is 1, the second is 2, ...
     * @param x
     *            the parameter value
     * @exception SQLException
     *                if a database access error occurs
     */
    @Override
    public void setTimestamp(int parameterIndex, java.sql.Timestamp x) throws SQLException {
        testIfClosed();
        statementHolder.setParameter(parameterIndex, x);
    }

    /**
     * <p>
     * Sets the value of the designated parameter using the given object. The
     * second parameter must be of type <code>Object</code>; therefore, the
     * <code>java.lang</code> equivalent objects should be used for built-in
     * types.
     * 
     * <p>
     * The JDBC specification specifies a standard mapping from Java
     * <code>Object</code> types to SQL types. The given argument will be
     * converted to the corresponding SQL type before being sent to the
     * database.
     * 
     * <p>
     * Note that this method may be used to pass datatabase- specific abstract
     * data types, by using a driver-specific Java type.
     * 
     * If the object is of a class implementing the interface
     * <code>SQLData</code>, the JDBC driver should call the method
     * <code>SQLData.writeSQL</code> to write it to the SQL data stream. If, on
     * the other hand, the object is of a class implementing <code>Ref</code>,
     * <code>Blob</code>, <code>Clob</code>, <code>Struct</code>, or
     * <code>Array</code>, the driver should pass it to the database as a value
     * of the corresponding SQL type.
     * <P>
     * This method throws an exception if there is an ambiguity, for example, if
     * the object is of a class implementing more than one of the interfaces
     * named above.
     * 
     * @param parameterIndex
     *            the first parameter is 1, the second is 2, ...
     * @param x
     *            the object containing the input parameter value
     * @exception SQLException
     *                if a database access error occurs or the type of the given
     *                object is ambiguous
     */
    @Override
    public void setObject(int parameterIndex, Object x) throws SQLException {
        testIfClosed();

        if (x != null && x.toString().length() > connectionHttp.getMaxLengthForString()) {
            throw new SQLException("Object is too big for upload: " + x.toString().length()
                    + " bytes. Maximum length authorized is: " + connectionHttp.getMaxLengthForString());
        }

        statementHolder.setParameter(parameterIndex, x);
    }

    /**
     * Sets the designated parameter to the given input stream. When a very
     * large binary value is input to a <code>LONGVARBINARY</code> parameter, it
     * may be more practical to send it via a <code>java.io.InputStream</code>
     * object. The data will be read from the stream as needed until end-of-file
     * is reached.
     * 
     * <P>
     * <B>Note:</B> This stream object can either be a standard Java stream
     * object or your own subclass that implements the standard interface.
     * <P>
     * <B>Note:</B> Consult your JDBC driver documentation to determine if it
     * might be more efficient to use a version of <code>setBinaryStream</code>
     * which takes a length parameter.
     * 
     * @param parameterIndex
     *            the first parameter is 1, the second is 2, ...
     * @param x
     *            the java input stream which contains the binary parameter
     *            value
     * @exception SQLException
     *                if parameterIndex does not correspond to a parameter
     *                marker in the SQL statement; if a database access error
     *                occurs or this method is called on a closed
     *                <code>PreparedStatement</code>
     * @throws SQLFeatureNotSupportedException
     *             if the JDBC driver does not support this method
     * @since 1.6
     */
    @Override
    public void setBinaryStream(int parameterIndex, java.io.InputStream x) throws SQLException {
        testIfClosed();
        this.setBinaryStream(parameterIndex, x, (long) -1);
    }

    /**
     * Add the files to the stack
     * 
     * @param file
     *            the local file, blob or clob
     * @param rawRemoteFileName
     *            the remote filename
     */
    protected void addFiles(File file, String rawRemoteFileName) {

        // HACK NDP
        if (!rawRemoteFileName.startsWith("/")) {
            rawRemoteFileName = "/" + rawRemoteFileName;
        }

        if (connectionHttp.isStatelessMode()) {
            connectionHttp.localFiles.add(file);
            connectionHttp.remoteFiles.add(rawRemoteFileName);
        } else {
            this.localFilesStatement.add(file);
            this.remoteFilesStatement.add(rawRemoteFileName);
        }
    }

    // /**
    // * Add InputStream to the stack
    // * @param in the input stream
    // * @param length the input stream length
    // * @param rawRemoteFileName the remote file naes
    // */
    // protected void addInputStreams(InputStream in, long length, String
    // rawRemoteFileName) {
    // if (connectionHttp.isStatelessMode()) {
    // connectionHttp.localInputStreams.add(in);
    // connectionHttp.localInputStreamLengths.add(length);
    // connectionHttp.remoteFiles.add(rawRemoteFileName);
    // } else {
    // this.localInputStreams.add(in);
    // this.localInputStreamLengths.add(length);
    // this.remoteFilesStatement.add(rawRemoteFileName);
    // }
    // }

    /**
     * Sets the designated parameter to the given input stream, which will have
     * the specified number of bytes. When a very large binary value is input to
     * a <code>LONGVARBINARY</code> parameter, it may be more practical to send
     * it via a <code>java.io.InputStream</code> object. The data will be read
     * from the stream as needed until end-of-file is reached.
     * 
     * <P>
     * <B>Note:</B> This stream object can either be a standard Java stream
     * object or your own subclass that implements the standard interface.
     * 
     * @param parameterIndex
     *            the first parameter is 1, the second is 2, ...
     * @param x
     *            the java input stream which contains the binary parameter
     *            value
     * @param length
     *            the number of bytes in the stream
     * @exception SQLException
     *                if parameterIndex does not correspond to a parameter
     *                marker in the SQL statement; if a database access error
     *                occurs or this method is called on a closed
     *                <code>PreparedStatement</code>
     */
    @Override
    public void setBinaryStream(int parameterIndex, java.io.InputStream x, int length) throws SQLException {
        testIfClosed();
        this.setBinaryStream(parameterIndex, x, (long) length);
    }

    /**
     * Sets the designated parameter to the given input stream, which will have
     * the specified number of bytes. When a very large binary value is input to
     * a <code>LONGVARBINARY</code> parameter, it may be more practical to send
     * it via a <code>java.io.InputStream</code> object. The data will be read
     * from the stream as needed until end-of-file is reached.
     * 
     * <P>
     * <B>Note:</B> This stream object can either be a standard Java stream
     * object or your own subclass that implements the standard interface.
     * 
     * @param parameterIndex
     *            the first parameter is 1, the second is 2, ...
     * @param x
     *            the java input stream which contains the binary parameter
     *            value
     * @param length
     *            the number of bytes in the stream
     * @exception SQLException
     *                if parameterIndex does not correspond to a parameter
     *                marker in the SQL statement; if a database access error
     *                occurs or this method is called on a closed
     *                <code>PreparedStatement</code>
     * @since 1.6
     */
    @Override
    public void setBinaryStream(int parameterIndex, java.io.InputStream x, long length) throws SQLException {

        testIfClosed();

        // Create the file from the input Stream, naming it on the table name
        FileNameFromBlobBuilder fileNameFromBlobBuilder = new FileNameFromBlobBuilder(sql, parameterIndex, false);
        String rawRemoteFileName = fileNameFromBlobBuilder.getFileName();

        String dir = FrameworkFileUtil.getKawansoftTempDir();
        File blobFile = new File(dir + File.separator + rawRemoteFileName);

        debug("rawRemoteFileName: " + rawRemoteFileName);
        debug("blobFile          : " + blobFile);

        OutputStream out = null;
        try {
            out = new BufferedOutputStream(new FileOutputStream(blobFile));
            IOUtils.copy(x, out);
        } catch (IOException e) {
            throw new SQLException(e.getMessage());
        } finally {
            IOUtils.closeQuietly(x);
            IOUtils.closeQuietly(out);
        }

        addFiles(blobFile, rawRemoteFileName);

        // Create the file from the input Stream, naming it on the table name
        // FileNameFromBlobBuilder fileNameFromBlobBuilder = new
        // FileNameFromBlobBuilder(
        // sql, parameterIndex, false);
        // String rawRemoteFileName = fileNameFromBlobBuilder.getFileName();
        //
        // addInputStreams(x, length, rawRemoteFileName);

        // Ok. File is successfully uploaded!
        // Set the parameter using the file name
        InputStream inputStream = new TransportInputStream(rawRemoteFileName);
        // parameterValues.put(parameterIndex, inputStream);
        statementHolder.setParameter(parameterIndex, inputStream);

    }

    @Override
    public void setBlob(int parameterIndex, Blob x) throws SQLException {
        testIfClosed();

        if (x instanceof BlobHttp) {

            BlobHttp blobHttp = (BlobHttp) x;
            // Close the underlying output stream, cleaner:
            blobHttp.close();

            String rawRemoteFileName = blobHttp.getFile().getName();

            debug("blobHttp.getFile(): " + blobHttp.getFile());
            debug("rawRemoteFileName : " + rawRemoteFileName);

            addFiles(blobHttp.getFile(), rawRemoteFileName);

            // Ok. File is successfully uploaded!
            // Set the parameter using the file name
            InputStream inputStream = new TransportInputStream(rawRemoteFileName);
            // parameterValues.put(parameterIndex, inputStream);
            statementHolder.setParameter(parameterIndex, inputStream);

        } else {
            InputStream in = x.getBinaryStream();
            setBinaryStream(parameterIndex, in, x.length());
        }

    }

    @Override
    public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException {
        testIfClosed();
        setBinaryStream(parameterIndex, inputStream);
    }

    @Override
    public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException {
        testIfClosed();
        setBinaryStream(parameterIndex, inputStream, length);
    }

    @Override
    public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException {
        setAsciiStream(parameterIndex, x);
    }

    @Override
    public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException {
        setAsciiStream(parameterIndex, x);
    }

    @Override
    public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException {
        testIfClosed();

        // Create the file from the input Stream, naming it on the table name
        FileNameFromBlobBuilder fileNameFromBlobBuilder = new FileNameFromBlobBuilder(sql, parameterIndex, true);
        String rawRemoteFileName = fileNameFromBlobBuilder.getFileName();

        String dir = FrameworkFileUtil.getKawansoftTempDir();
        File clobFile = new File(dir + File.separator + rawRemoteFileName);

        debug("rawRemoteFileName: " + rawRemoteFileName);
        debug("blobFile          : " + clobFile);

        Writer writer = null;

        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(x));
            writer = new BufferedWriter(new FileWriter(clobFile));
            String line = null;
            while ((line = bufferedReader.readLine()) != null) {
                if (connectionHttp.isHtmlEncodingOn()) {
                    line = HtmlConverter.toHtml(line);
                }
                writer.write(line + CR_LF);
            }
        } catch (IOException e) {
            throw new SQLException(e.getMessage());
        } finally {
            IOUtils.closeQuietly(x);
            IOUtils.closeQuietly(writer);
        }

        addFiles(clobFile, rawRemoteFileName);

        // Ok. File is successfully uploaded!
        // Set the parameter using the file name
        TransportAsciiStream transportAsciiStream = new TransportAsciiStream(rawRemoteFileName);

        // parameterValues.put(parameterIndex, inputStream);
        statementHolder.setParameter(parameterIndex, transportAsciiStream);
    }

    /**
     * Sets the designated parameter to the given <code>Reader</code> object.
     * When a very large UNICODE value is input to a <code>LONGVARCHAR</code>
     * parameter, it may be more practical to send it via a
     * <code>java.io.Reader</code> object. The data will be read from the stream
     * as needed until end-of-file is reached. The JDBC driver will do any
     * necessary conversion from UNICODE to the database char format.
     * 
     * <P>
     * <B>Note:</B> This stream object can either be a standard Java stream
     * object or your own subclass that implements the standard interface.
     * <P>
     * <B>Note:</B> Consult your JDBC driver documentation to determine if it
     * might be more efficient to use a version of
     * <code>setCharacterStream</code> which takes a length parameter.
     * 
     * @param parameterIndex
     *            the first parameter is 1, the second is 2, ...
     * @param reader
     *            the <code>java.io.Reader</code> object that contains the
     *            Unicode data
     * @exception SQLException
     *                if parameterIndex does not correspond to a parameter
     *                marker in the SQL statement; if a database access error
     *                occurs or this method is called on a closed
     *                <code>PreparedStatement</code>
     * @throws SQLFeatureNotSupportedException
     *             if the JDBC driver does not support this method
     * @since 1.6
     */
    @Override
    public void setCharacterStream(int parameterIndex, java.io.Reader reader) throws SQLException {
        testIfClosed();

        // Create the file from the input Stream, naming it on the table name
        FileNameFromBlobBuilder fileNameFromBlobBuilder = new FileNameFromBlobBuilder(sql, parameterIndex, true);
        String rawRemoteFileName = fileNameFromBlobBuilder.getFileName();

        String dir = FrameworkFileUtil.getKawansoftTempDir();
        File clobFile = new File(dir + File.separator + rawRemoteFileName);

        debug("rawFileName: " + rawRemoteFileName);
        debug("clobFile   : " + clobFile);

        BufferedReader bufferedReader = null;
        Writer writer = null;
        try {
            bufferedReader = new BufferedReader(reader);
            writer = new BufferedWriter(new FileWriter(clobFile));
            String line = null;
            while ((line = bufferedReader.readLine()) != null) {
                if (connectionHttp.isHtmlEncodingOn()) {
                    line = HtmlConverter.toHtml(line);
                }
                writer.write(line + CR_LF);
            }
        } catch (IOException e) {
            throw new SQLException(e.getMessage());
        } finally {
            IOUtils.closeQuietly(reader);
            IOUtils.closeQuietly(writer);
        }

        addFiles(clobFile, rawRemoteFileName);

        // Ok. File is successfully uploaded!
        // Set the parameter using the file name
        Reader transportReader = new TransportReader(rawRemoteFileName);
        statementHolder.setParameter(parameterIndex, transportReader);
    }

    /**
     * Sets the designated parameter to the given <code>Reader</code> object,
     * which is the given number of characters long. When a very large UNICODE
     * value is input to a <code>LONGVARCHAR</code> parameter, it may be more
     * practical to send it via a <code>java.io.Reader</code> object. The data
     * will be read from the stream as needed until end-of-file is reached. The
     * JDBC driver will do any necessary conversion from UNICODE to the database
     * char format.
     * 
     * <P>
     * <B>Note:</B> This stream object can either be a standard Java stream
     * object or your own subclass that implements the standard interface.
     * 
     * @param parameterIndex
     *            the first parameter is 1, the second is 2, ...
     * @param reader
     *            the <code>java.io.Reader</code> object that contains the
     *            Unicode data
     * @param length
     *            the number of characters in the stream
     * @exception SQLException
     *                if parameterIndex does not correspond to a parameter
     *                marker in the SQL statement; if a database access error
     *                occurs or this method is called on a closed
     *                <code>PreparedStatement</code>
     * @since 1.2
     */
    @Override
    public void setCharacterStream(int parameterIndex, java.io.Reader reader, int length) throws SQLException {
        testIfClosed();
        this.setCharacterStream(parameterIndex, reader);
    }

    /**
     * Sets the designated parameter to the given <code>Reader</code> object,
     * which is the given number of characters long. When a very large UNICODE
     * value is input to a <code>LONGVARCHAR</code> parameter, it may be more
     * practical to send it via a <code>java.io.Reader</code> object. The data
     * will be read from the stream as needed until end-of-file is reached. The
     * JDBC driver will do any necessary conversion from UNICODE to the database
     * char format.
     * 
     * <P>
     * <B>Note:</B> This stream object can either be a standard Java stream
     * object or your own subclass that implements the standard interface.
     * 
     * @param parameterIndex
     *            the first parameter is 1, the second is 2, ...
     * @param reader
     *            the <code>java.io.Reader</code> object that contains the
     *            Unicode data
     * @param length
     *            the number of characters in the stream
     * @exception SQLException
     *                if parameterIndex does not correspond to a parameter
     *                marker in the SQL statement; if a database access error
     *                occurs or this method is called on a closed
     *                <code>PreparedStatement</code>
     * @since 1.6
     */
    @Override
    public void setCharacterStream(int parameterIndex, java.io.Reader reader, long length) throws SQLException {
        testIfClosed();
        this.setCharacterStream(parameterIndex, reader);
    }

    @Override
    public void setClob(int parameterIndex, Clob x) throws SQLException {
        testIfClosed();
        // Clob creation is not optimized (other file creation) , because if
        // htmlEncoding is on we must rewrite the file
        Reader reader = x.getCharacterStream();
        setCharacterStream(parameterIndex, reader);
    }

    @Override
    public void setClob(int parameterIndex, Reader reader, long length) throws SQLException {
        testIfClosed();
        setCharacterStream(parameterIndex, reader, length);
    }

    @Override
    public void setClob(int parameterIndex, Reader reader) throws SQLException {
        testIfClosed();
        setCharacterStream(parameterIndex, reader);
    }

    /**
     * Clears the current parameter values immediately.
     * <P>
     * In general, parameter values remain in force for repeated use of a
     * statement. Setting a parameter value automatically clears its previous
     * value. However, in some cases it is useful to immediately release the
     * resources used by the current parameter values; this can be done by
     * calling the method <code>clearParameters</code>.
     * 
     * @exception SQLException
     *                if a database access error occurs
     */
    @Override
    public void clearParameters() throws SQLException {
        statementHolder.clearParameters();
    }

    // --------------------------JDBC 2.0-----------------------------
    /**
     * Adds a set of parameters to this <code>PreparedStatement</code> object's
     * batch of commands.
     * 
     * @exception SQLException
     *                if a database access error occurs
     * @see Statement#addBatch
     * @since 1.2
     */
    @Override
    public void addBatch() throws SQLException {

        // Check that all parameters values are set
        ParametersUtil.checkParameters(statementHolder);

        // Create a StatementHolder that contains only the parameter
        // and add it to the batchHolderList field:

        StatementHolder batchHolder = new StatementHolder(statementHolder.getParameterTypes(),
                statementHolder.getParameterStringValues(), statementHolder.isHtmlEncodingOn());

        batchHolder.setPreparedStatement(true);
        batchHolder.setExecuteUpdate(true);

        batchHolderFileList.add(batchHolder);

        debug("batchHolder: " + batchHolder);

    }

    /**
     * Submits a batch of commands to the database for execution and if all
     * commands execute successfully, returns an array of update counts. The
     * <code>int</code> elements of the array that is returned are ordered to
     * correspond to the commands in the batch, which are ordered according to
     * the order in which they were added to the batch. The elements in the
     * array returned by the method <code>executeBatch</code> may be one of the
     * following:
     * <OL>
     * <LI>A number greater than or equal to zero -- indicates that the command
     * was processed successfully and is an update count giving the number of
     * rows in the database that were affected by the command's execution
     * <LI>A value of <code>SUCCESS_NO_INFO</code> -- indicates that the command
     * was processed successfully but that the number of rows affected is
     * unknown
     * <P>
     * If one of the commands in a batch update fails to execute properly, this
     * method throws a <code>BatchUpdateException</code>, and a JDBC driver may
     * or may not continue to process the remaining commands in the batch.
     * However, the driver's behavior must be consistent with a particular DBMS,
     * either always continuing to process commands or never continuing to
     * process commands. If the driver continues processing after a failure, the
     * array returned by the method
     * <code>BatchUpdateException.getUpdateCounts</code> will contain as many
     * elements as there are commands in the batch, and at least one of the
     * elements will be the following:
     * <P>
     * <LI>A value of <code>EXECUTE_FAILED</code> -- indicates that the command
     * failed to execute successfully and occurs only if a driver continues to
     * process commands after a command fails
     * </OL>
     * <P>
     * A driver is not required to implement this method. The possible
     * implementations and return values have been modified in the Java 2 SDK,
     * Standard Edition, version 1.3 to accommodate the option of continuing to
     * proccess commands in a batch update after a
     * <code>BatchUpdateException</code> obejct has been thrown.
     * 
     * @return an array of update counts containing one element for each command
     *         in the batch. The elements of the array are ordered according to
     *         the order in which commands were added to the batch.
     * @exception SQLException
     *                if a database access error occurs or the driver does not
     *                support batch statements. Throws
     *                {@link BatchUpdateException} (a subclass of
     *                <code>SQLException</code>) if one of the commands sent to
     *                the database fails to execute properly or attempts to
     *                return a result set.
     * @since 1.3
     */
    @Override
    public int[] executeBatch() throws SQLException {
        int updateCounts[] = new int[batchHolderFileList.size()];

        if (batchHolderFileList.size() == 0) {
            return updateCounts;
        }

        JdbcHttpBatchTransfer jdbcHttpBatchTransfer = new JdbcHttpBatchTransfer(connectionHttp,
                connectionHttp.getAuthenticationToken());

        String updateCountsStr = jdbcHttpBatchTransfer
                .getStringFromExecutePrepStatementBatchOnServer(statementHolder, batchHolderFileList);
        updateCounts = IntArrayTransport.fromJson(updateCountsStr);

        clearBatch();
        return updateCounts;
    }

    /**
     * Sets the designated parameter to the given <code>java.net.URL</code>
     * value. The driver converts this to an SQL <code>DATALINK</code> value
     * when it sends it to the database.
     *
     * @param parameterIndex
     *            the first parameter is 1, the second is 2, ...
     * @param x
     *            the <code>java.net.URL</code> object to be set
     * @exception SQLException
     *                if parameterIndex does not correspond to a parameter
     *                marker in the SQL statement; if a database access error
     *                occurs or this method is called on a closed
     *                <code>PreparedStatement</code>
     * @throws SQLFeatureNotSupportedException
     *             if the JDBC driver does not support this method
     * @since 1.4
     */
    @Override
    public void setURL(int parameterIndex, URL x) throws SQLException {

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

        statementHolder.setParameter(parameterIndex, x);
    }

    /**
     * Sets the designated parameter to the given <code>java.sql.Array</code>
     * object. The driver converts this to an SQL <code>ARRAY</code> value when
     * it sends it to the database.
     *
     * @param parameterIndex
     *            the first parameter is 1, the second is 2, ...
     * @param x
     *            an <code>Array</code> object that maps an SQL
     *            <code>ARRAY</code> value
     * @exception SQLException
     *                if parameterIndex does not correspond to a parameter
     *                marker in the SQL statement; if a database access error
     *                occurs or this method is called on a closed
     *                <code>PreparedStatement</code>
     * @throws SQLFeatureNotSupportedException
     *             if the JDBC driver does not support this method
     * @since 1.2
     */
    public void setArray(int parameterIndex, Array x) throws SQLException {

        if (connectionHttp.isStatelessMode()) {
            throw new SQLException(ConnectionHttp.FEATURE_NOT_SUPPORTED_IN_STATELESS_MODE);
        }

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

        statementHolder.setParameter(parameterIndex, x);
    }

    /**
     * Sets the designated parameter to the given <code>java.sql.RowId</code>
     * object. The driver converts this to a SQL <code>ROWID</code> value when
     * it sends it to the database
     *
     * @param parameterIndex
     *            the first parameter is 1, the second is 2, ...
     * @param x
     *            the parameter value
     * @throws SQLException
     *             if parameterIndex does not correspond to a parameter marker
     *             in the SQL statement; if a database access error occurs or
     *             this method is called on a closed
     *             <code>PreparedStatement</code>
     * @throws SQLFeatureNotSupportedException
     *             if the JDBC driver does not support this method
     *
     * @since 1.6
     */
    @Override
    public void setRowId(int parameterIndex, RowId x) throws SQLException {
        if (connectionHttp.isStatelessMode()) {
            throw new SQLException(ConnectionHttp.FEATURE_NOT_SUPPORTED_IN_STATELESS_MODE);
        }

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

        statementHolder.setParameter(parameterIndex, x);
    }

    //
    // Not (yet) implemented methods
    //

    @Override
    public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException {
        throw new SQLFeatureNotSupportedException(ConnectionHttp.KAWANFW_NOT_SUPPORTED_METHOD);
    }

    @Override
    public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException {
        throw new SQLFeatureNotSupportedException(ConnectionHttp.KAWANFW_NOT_SUPPORTED_METHOD);
    }

    @Override
    public void setRef(int parameterIndex, Ref x) throws SQLException {
        throw new SQLFeatureNotSupportedException(ConnectionHttp.KAWANFW_NOT_SUPPORTED_METHOD);
    }

    @Override
    public ResultSetMetaData getMetaData() throws SQLException {
        throw new SQLFeatureNotSupportedException(ConnectionHttp.KAWANFW_NOT_SUPPORTED_METHOD);
    }

    @Override
    public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException {
        throw new SQLFeatureNotSupportedException(ConnectionHttp.KAWANFW_NOT_SUPPORTED_METHOD);
    }

    @Override
    public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException {
        throw new SQLFeatureNotSupportedException(ConnectionHttp.KAWANFW_NOT_SUPPORTED_METHOD);
    }

    @Override
    public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException {
        throw new SQLFeatureNotSupportedException(ConnectionHttp.KAWANFW_NOT_SUPPORTED_METHOD);
    }

    @Override
    public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException {
        throw new SQLFeatureNotSupportedException(ConnectionHttp.KAWANFW_NOT_SUPPORTED_METHOD);
    }

    @Override
    public ParameterMetaData getParameterMetaData() throws SQLException {
        throw new SQLFeatureNotSupportedException(ConnectionHttp.KAWANFW_NOT_SUPPORTED_METHOD);
    }

    @Override
    public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException {
        throw new SQLFeatureNotSupportedException(ConnectionHttp.KAWANFW_NOT_SUPPORTED_METHOD);
    }

    @Override
    public void setNClob(int parameterIndex, NClob value) throws SQLException {
        throw new SQLFeatureNotSupportedException(ConnectionHttp.KAWANFW_NOT_SUPPORTED_METHOD);
    }

    @Override
    public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException {
        throw new SQLFeatureNotSupportedException(ConnectionHttp.KAWANFW_NOT_SUPPORTED_METHOD);
    }

    @Override
    public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException {
        throw new SQLFeatureNotSupportedException(ConnectionHttp.KAWANFW_NOT_SUPPORTED_METHOD);
    }

    @Override
    public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) throws SQLException {
        throw new SQLFeatureNotSupportedException(ConnectionHttp.KAWANFW_NOT_SUPPORTED_METHOD);
    }

    @Override
    public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException {
        throw new SQLFeatureNotSupportedException(ConnectionHttp.KAWANFW_NOT_SUPPORTED_METHOD);
    }

    @Override
    public void setNClob(int parameterIndex, Reader reader) throws SQLException {
        throw new SQLFeatureNotSupportedException(ConnectionHttp.KAWANFW_NOT_SUPPORTED_METHOD);
    }

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

}