package com.pearson.statsagg.utilities.db_utils;

import com.pearson.statsagg.utilities.core_utils.StackTrace;
import com.google.common.collect.Lists;
import java.io.BufferedReader;
import java.math.BigDecimal;
import java.sql.BatchUpdateException;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.Ref;
import java.sql.ResultSet;
import java.sql.RowId;
import java.sql.SQLXML;
import java.sql.Statement;
import java.sql.Time;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Jeffrey Schmidt
 */
public class DatabaseUtils {
    
    private static final Logger logger = LoggerFactory.getLogger(DatabaseUtils.class.getName());
    
    public static final byte CLOSE = 1;
    public static final byte CLOSE_AND_COMMIT = 2;
    public static final byte CLOSE_AND_ROLLBACK = 3;
    
    public static void cleanup(Connection connection) {
        cleanup(null, null, null, null, CLOSE);
    }
    
    public static void cleanup(ResultSet results) {
        cleanup(null, null, null, results, CLOSE);
    }
    
    public static void cleanup(Statement statement) {
        cleanup(null, null, statement, null, CLOSE);
    }
    
    public static void cleanup(Statement statement, ResultSet results) {
        cleanup(null, null, statement, results, CLOSE);
    }
    
    public static void cleanup(Connection connection, Statement statement) {
        cleanup(connection, null, statement, null, CLOSE);
    }
    
    public static void cleanup(Connection connection, Statement statement, byte closeMode) {
        cleanup(connection, null, statement, null, closeMode);
    }

    public static void cleanup(Connection connection, Statement statement, ResultSet results) {
        cleanup(connection, null, statement, results, CLOSE);
    }
    
    public static void cleanup(Connection connection, Statement statement, ResultSet results, byte closeMode) {
        cleanup(connection, null, statement, results, closeMode);
    }

    public static void cleanup(PreparedStatement preparedStatement) {
        cleanup(null, preparedStatement, null, null, CLOSE);
    }
    
    public static void cleanup(PreparedStatement preparedStatement, ResultSet results) {
        cleanup(null, preparedStatement, null, results, CLOSE);
    }
    
    public static void cleanup(Connection connection, PreparedStatement preparedStatement) {
        cleanup(connection, preparedStatement, null, null, CLOSE);
    }
    
    public static void cleanup(Connection connection, PreparedStatement preparedStatement, byte closeMode) {
        cleanup(connection, preparedStatement, null, null, closeMode);
    }
    
    public static void cleanup(Connection connection, PreparedStatement preparedStatement, ResultSet results) {
        cleanup(connection, preparedStatement, null, results, CLOSE);
    }
    
    public static void cleanup(Connection connection, PreparedStatement preparedStatement, ResultSet results, byte closeMode) {
        cleanup(connection, preparedStatement, null, results, closeMode);
    }

    public static void cleanup(Connection connection, PreparedStatement preparedStatement, Statement statement, ResultSet results, byte closeMode) {
        
        if (results != null) {
            try {
                results.close();
            }
            catch (Exception e) {
                logger.error(e.toString() + System.lineSeparator() + StackTrace.getStringFromStackTrace(e));
            }
            finally {
                results = null;
            }
        }
        
        if (preparedStatement != null) {
            try {
                preparedStatement.close();
            }
            catch (Exception e) {
                logger.error(e.toString() + System.lineSeparator() + StackTrace.getStringFromStackTrace(e));
            }
            finally {
                preparedStatement = null;
            }
        }
        
        if (statement != null) {
            try {
                statement.close();
            }
            catch (Exception e) {
                logger.error(e.toString() + System.lineSeparator() + StackTrace.getStringFromStackTrace(e));
            }
            finally {
                statement = null;
            }
        }
        
        if (connection != null) {
            try {
                if (closeMode == CLOSE_AND_COMMIT) {
                    disconnectAndCommit(connection);
                }
                else if (closeMode == CLOSE_AND_ROLLBACK) {
                    disconnectAndRollback(connection);
                }
                else disconnect(connection);
            }
            catch (Exception e) {
                logger.error(e.toString() + System.lineSeparator() + StackTrace.getStringFromStackTrace(e));
            }
            finally {
                connection = null;
            }
        }
    }
    
    public static Connection connect(String jdbcString) {
        
        Connection connection = null;
        
        try {
            connection = DriverManager.getConnection(jdbcString);
        }
        catch (Exception e) {
            logger.error(e.toString() + System.lineSeparator() + StackTrace.getStringFromStackTrace(e));
        }
        
        return connection;
    }
    
    public static Connection connect(String url, String username, String password) {
        
        Connection connection = null;
        
        try {
            connection = DriverManager.getConnection(url, username, password);
        }
        catch (Exception e) {
            logger.error(e.toString() + System.lineSeparator() + StackTrace.getStringFromStackTrace(e));
        }
        
        return connection;
    }
    
    public static boolean disconnect(Connection connection) {
        
        try {
            if (connection != null) connection.close();
            connection = null;
            return true;
        }
        catch (Exception e) {
            logger.error(e.toString() + System.lineSeparator() + StackTrace.getStringFromStackTrace(e));            
            connection = null;
            return false;
        }
        
    }
    
    public static boolean disconnectAndCommit(Connection connection) {
        commit(connection);
        return disconnect(connection);
    }
    
    public static boolean disconnectAndRollback(Connection connection) {
        rollback(connection);
        return disconnect(connection);
    }
    
    public static boolean isResultSetValid(ResultSet results) {
        try {
            if ((results == null) || results.isClosed()) {
                return false;
            }
            else {
                return true;
            }
        }
        catch (Exception e) {
            logger.error(e.toString() + System.lineSeparator() + StackTrace.getStringFromStackTrace(e));
            return false;
        }   
    }
    
    public static Boolean isConnectionReadOnly(Connection connection) {
        try {
            if ((connection == null) || connection.isClosed()) {
                return null;
            }

            return connection.isReadOnly();
        }
        catch (Exception e) {
            return null;
        }
    }
    
    
    public static boolean isConnectionValid(Connection connection) {
        return isConnectionValid(connection, 0);
    }
    
    public static boolean isConnectionValid(Connection connection, int timeoutInSeconds) {
        
        long startTime = System.currentTimeMillis();
        
        try {
            if (connection == null) {
                return false;
            }
            else if (!connection.isValid(timeoutInSeconds)) {
                return false;
            }
            else {
                return true;
            }
        }
        catch (Exception e) {
            long timeElapsed = System.currentTimeMillis() - startTime;
            
            logger.error("Method=IsConnectionValid" + ", TimeElapsed=" + timeElapsed + ", Exception=" + e.toString() + 
                    System.lineSeparator() + StackTrace.getStringFromStackTrace(e));
            
            return false;
        } 
        
    }
    
    public static void setAutoCommit(Connection connection, boolean enabled) {
        
        if (connection == null) {
            return;
        }
        
        try {
            connection.setAutoCommit(enabled);
        }
        catch (Exception e) {
            logger.error(e.toString() + System.lineSeparator() + StackTrace.getStringFromStackTrace(e));
        }
        
    }
    
    public static <T extends DatabaseResultSetHandler, S extends DatabaseObject> List<S> query_Statement(Connection connection, T t, String sql) {
        return query_Statement(connection, t, 1000, sql);
    }
    
    // returns null if there was a fatal error. otherwise returns a list of results.
    public static <T extends DatabaseResultSetHandler, S extends DatabaseObject> List<S> query_Statement(Connection connection, T t, int fetchSize, String sql) {
        
        if (connection == null) {
            logger.error("Connection object cannot be null");
            return null;
        }
        
        if ((sql == null) || sql.isEmpty()) {
            logger.error("Sql cannot be null or empty");
            return null;
        }
        
        if (fetchSize < 1) {
            logger.error("fetchSize cannot be less than 1");
            return null;
        }

        List<S> resultsList = new ArrayList<>();
        Statement statement = null;
        ResultSet resultSet = null;

        try {
            statement = connection.createStatement();
            statement.setFetchSize(fetchSize);
            resultSet = statement.executeQuery(sql);           
            resultsList = t.handleResultSet(resultSet);
            return resultsList;
        }
        catch (Exception e) {
            logger.error(e.toString() + System.lineSeparator() + StackTrace.getStringFromStackTrace(e));   
        }      
        finally {
            cleanup(connection, statement, resultSet);
        }
        
        return resultsList;
    }
    
    public static <T extends DatabaseResultSetHandler, S extends DatabaseObject> List<S> query_PreparedStatement(Connection connection, T t, String sql, List<Object> preparedStatementParameters) {
        return query_PreparedStatement(connection, t, 1000, sql, preparedStatementParameters);
    }
    
    public static <T extends DatabaseResultSetHandler, S extends DatabaseObject> List<S> query_PreparedStatement(Connection connection, T t, String sql, Object... preparedStatementParameters) {
        return query_PreparedStatement(connection, t, 1000, sql, getParametersAsListOfObjects(preparedStatementParameters));
    }
    
    // returns null if there was a fatal error. otherwise returns a list of results.
    public static <T extends DatabaseResultSetHandler, S extends DatabaseObject> List<S> query_PreparedStatement(Connection connection, T t, int fetchSize, String sql, List<Object> preparedStatementParameters) {
        
        if (connection == null) {
            logger.error("Connection object cannot be null");
            return null;
        }
        
        if ((sql == null) || sql.isEmpty()) {
            logger.error("Sql cannot be null or empty");
            return null;
        }
        
        if (t == null) {
            logger.error("DatabaseResultSetHandler cannot be null");
            return null;
        }
        
        if (fetchSize < 1) {
            logger.error("fetchSize cannot be less than 1");
            return null;
        }
        
        List<S> resultsList = new ArrayList<>();
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setFetchSize(fetchSize);
            setPreparedStatementParameters(preparedStatement, preparedStatementParameters);
            resultSet = preparedStatement.executeQuery();
            resultsList = t.handleResultSet(resultSet);
            return resultsList;
        }
        catch (Exception e) {
            logger.error(e.toString() + System.lineSeparator() + StackTrace.getStringFromStackTrace(e));   
        }      
        finally {
            cleanup(connection, preparedStatement, resultSet);
        }
        
        return resultsList;
    }
    
    public static int dml_PreparedStatement(Connection connection, String sql, List<Object> parameters) {
        return genericDmlStatement_SingleStatement(connection, true, true, sql, parameters);
    }
    
    public static int dml_PreparedStatement(Connection connection, String sql, Object... parameters) {
        return genericDmlStatement_SingleStatement(connection, true, true, sql, getParametersAsListOfObjects(parameters));
    }
    
    public static int dml_PreparedStatement(Connection connection, boolean cleanupOnCompletion, boolean commitOnCompletion, String sql, List<Object> parameters) {
        return genericDmlStatement_SingleStatement(connection, cleanupOnCompletion, commitOnCompletion, sql, parameters);
    }
    
    public static int dml_PreparedStatement(Connection connection, boolean cleanupOnCompletion, boolean commitOnCompletion, String sql, Object... parameters) {
        return genericDmlStatement_SingleStatement(connection, cleanupOnCompletion, commitOnCompletion, sql, getParametersAsListOfObjects(parameters));
    }

    // returns affected row count if successful
    // returns -1 if there was an error. no rollback is performed.
    private static int genericDmlStatement_SingleStatement(Connection connection, boolean cleanupOnCompletion, boolean commitOnCompletion, String sql, List<Object> parameters) {

        if (connection == null) {
            logger.error("Connection object cannot be null");
            return -1;
        }
        
        if ((sql == null) || sql.isEmpty()) {
            logger.error("Sql cannot be null or empty");
            return -1;
        }
        
        PreparedStatement preparedStatement = null;
        
        try {
            preparedStatement = connection.prepareStatement(sql);
            setPreparedStatementParameters(preparedStatement, parameters);
            int affectedRowCounts = preparedStatement.executeUpdate();
            
            if (commitOnCompletion) {
                boolean wasCommitSuccess = commit(connection);
                if (wasCommitSuccess) return affectedRowCounts;
                else return -1;
            }
            else return affectedRowCounts;
        }
        catch (Exception e) {
            logger.error(e.toString() + System.lineSeparator() + StackTrace.getStringFromStackTrace(e));   
            return -1;
        }      
        finally {
            cleanup(preparedStatement);
            if (cleanupOnCompletion) cleanup(connection, preparedStatement);
        }
        
    }

    public static List<Integer> dml_PreparedStatement_Batch(Connection connection, String sql, List<List<Object>> listOfParameters) {
        return genericDmlStatement_Batch(connection, true, true, Integer.MAX_VALUE, false, sql, listOfParameters);
    }
    
    public static List<Integer> dml_PreparedStatement_Batch(Connection connection, int batchSize, String sql, List<List<Object>> listOfParameters) {
        return genericDmlStatement_Batch(connection, true, true, batchSize, true, sql, listOfParameters);
    }
    
    public static List<Integer> dml_PreparedStatement_Batch(Connection connection, boolean cleanupOnCompletion, boolean commitOnCompletion,
            int batchSize, boolean commitOnEveryBatch, String sql, List<List<Object>> listOfParameters) {
        return genericDmlStatement_Batch(connection, cleanupOnCompletion, commitOnCompletion, batchSize, commitOnEveryBatch, sql, listOfParameters);
    }
    
    // returns a list of affected row counts if successful
    // returns null if there was a fatal error
    private static List<Integer> genericDmlStatement_Batch(Connection connection, boolean cleanupOnCompletion, boolean commitOnCompletion,
            int batchSize, boolean commitOnEveryBatch, String sql, List<List<Object>> listOfParameters) {

        if (connection == null) {
            logger.error("Connection object cannot be null");
            return null;
        }
        
        if ((sql == null) || sql.isEmpty()) {
            logger.error("Sql cannot be null or empty");
            return null;
        }
        
        if (listOfParameters == null){
            logger.error("List of parameters cannot be null");
            return null;
        }
        
        PreparedStatement preparedStatement = null;
        Boolean isAutocommit = null;
        
        try {
            preparedStatement = connection.prepareStatement(sql);
            isAutocommit = connection.getAutoCommit();
            connection.setAutoCommit(false);
            
            List<Integer> affectedRowCounts_List = new ArrayList<>();
            List<List<List<Object>>> listOfParameters_Batches = Lists.partition(listOfParameters, batchSize);
            
            for (List<List<Object>> parametersBatch : listOfParameters_Batches) {
                // add the parameters for every record of the batch into the PreparedStatement
                for (List<Object> parameters : parametersBatch) {
                    try {
                        if (parameters == null) setPreparedStatementParameters(preparedStatement, new ArrayList<>());
                        else setPreparedStatementParameters(preparedStatement, parameters);
                        preparedStatement.addBatch();
                    }
                    catch (Exception e) {
                        logger.error(e.toString() + System.lineSeparator() + StackTrace.getStringFromStackTrace(e));  
                    }
                    
                    preparedStatement.clearParameters();
                }
                
                // execute the batch of dml & store the results array
                int[] affectedRowCounts = null;
                try {
                    affectedRowCounts = preparedStatement.executeBatch();
                }
                catch (BatchUpdateException bue) { // if there's an error, record which rows had errors & which didn't
                    logger.error(bue.toString() + System.lineSeparator() + StackTrace.getStringFromStackTrace(bue));  
                    
                    if (bue.getUpdateCounts().length < parametersBatch.size()) {
                        affectedRowCounts = new int[parametersBatch.size()];
                        int i;
                        for (i = 0; i < bue.getUpdateCounts().length; i++) affectedRowCounts[i] = bue.getUpdateCounts()[i];
                        for (; i < affectedRowCounts.length; i++) affectedRowCounts[i] = Statement.EXECUTE_FAILED;
                    }
                    else {
                        affectedRowCounts = bue.getUpdateCounts();
                    }
                }
                catch (Exception e) {
                    logger.error(e.toString() + System.lineSeparator() + StackTrace.getStringFromStackTrace(e));  
                }
                
                // commit the batch (if configured to do so). if the commit fails, mark every row in the batch as failed in the results.
                if (commitOnEveryBatch && (affectedRowCounts != null)) {
                    boolean wasCommitSuccess = commit(connection, false);
                    
                    if (wasCommitSuccess) {
                        for (int i = 0; i < affectedRowCounts.length; i++) {
                            Integer affectedRowCount = affectedRowCounts[i];
                            affectedRowCounts_List.add(affectedRowCount);
                        }
                    }
                    else {
                        for (int i = 0; i < affectedRowCounts.length; i++) {
                            affectedRowCounts_List.add(Statement.EXECUTE_FAILED);
                        }
                    }
                }
                else if (affectedRowCounts != null) { // we didn't commit, so store the results of the executeBatch call
                    for (int i = 0; i < affectedRowCounts.length; i++) {
                        Integer affectedRowCount = affectedRowCounts[i];
                        affectedRowCounts_List.add(affectedRowCount);
                    }
                }
                else { // the results of the executeBatch command are invalid, set the results for every record for this batch as failed
                    for (int i = 0; i < parametersBatch.size(); i++) {
                        affectedRowCounts_List.add(Statement.EXECUTE_FAILED);
                    }
                }
            }

            // if we need to commit, but opted to not commit on every individual batch, then the commit happens here. if the commit fails, all results will be marked as failed.
            if (!commitOnEveryBatch && commitOnCompletion) {
                boolean wasCommitSuccess = commit(connection, false);
                
                if (wasCommitSuccess) {
                    return affectedRowCounts_List;
                }
                else {
                    for (int i = 0; i < affectedRowCounts_List.size(); i++) {
                        affectedRowCounts_List.clear();
                        affectedRowCounts_List.add(Statement.EXECUTE_FAILED);
                    }
                }
            }
            
            return affectedRowCounts_List;
        }
        catch (Exception e) {
            logger.error(e.toString() + System.lineSeparator() + StackTrace.getStringFromStackTrace(e));   
            return null;
        }      
        finally {
            // if the connection is not being closed, set autocommit back to whatever it was before this method started
            if (!cleanupOnCompletion) {
                try {
                    if (isAutocommit != null) {
                        connection.setAutoCommit(isAutocommit);
                    }
                }
                catch (Exception e) {
                    logger.error(e.toString() + System.lineSeparator() + StackTrace.getStringFromStackTrace(e)); 
                }
            }
            
            cleanup(preparedStatement);
            if (cleanupOnCompletion) cleanup(connection, preparedStatement);
        }
        
    }

    public static boolean commit(Connection connection, boolean checkAutocommit) {
        
        if (connection == null) {
            return false;
        }
        
        try {
            if (!checkAutocommit) connection.commit(); 
            else if (!connection.getAutoCommit()) connection.commit(); 
            return true;
        }
        catch (Exception e) {
            logger.error(e.toString() + System.lineSeparator() + StackTrace.getStringFromStackTrace(e));
            return false;
        }
        
    }
    
    public static boolean commit(Connection connection) {
        return commit(connection, true);
    }
    
    public static boolean rollback(Connection connection, boolean checkAutocommit) {
        
        if (connection == null) {
            return false;
        }
        
        try {
            if (!checkAutocommit) connection.rollback(); 
            else if (!connection.getAutoCommit()) connection.rollback(); 
            return true;
        }
        catch (Exception e) {
            logger.error(e.toString() + System.lineSeparator() + StackTrace.getStringFromStackTrace(e));
            return false;
        }
    }
    
    public static boolean rollback(Connection connection) {
        return rollback(connection, true);
    }
    
    public static boolean setPreparedStatementParameters(PreparedStatement preparedStatement, List<Object> preparedStatementParameters) {
        
        if (preparedStatement == null) {
            logger.warn("Can't set PreparedStatementParameters - PreparedStatement is null");
            return false;
        } 
        
        if (preparedStatementParameters == null) {
            preparedStatementParameters = new ArrayList<>();
        }
        
        boolean wasSuccess = true;
        
        try {
            AtomicInteger index = new AtomicInteger(1);

            for (Object object : preparedStatementParameters) {
                int result = setPreparedStatementParameter(preparedStatement, preparedStatementParameters, object, index);
                if (result == -1) wasSuccess = false;
            }
        }
        catch (Exception e) {
            logger.error(e.toString() + System.lineSeparator() + StackTrace.getStringFromStackTrace(e));
        }   
        
        return wasSuccess;
    }
    
    private static int setPreparedStatementParameter(PreparedStatement preparedStatement, List<Object> preparedStatementParameters, Object object, AtomicInteger index) {
        
        if ((preparedStatement == null) || (preparedStatementParameters == null)) {
            logger.warn("Can't set preparedStatementParameters - preparedStatementParameters or preparedStatement is null");
            return -1;
        } 

        try {
            if (object == null) {
                preparedStatement.setObject(index.getAndIncrement(), null);
            } 
            else if (object instanceof BigDecimal) {
                preparedStatement.setBigDecimal(index.getAndIncrement(), (BigDecimal) object);
            }
            else if (object instanceof Blob) {
                preparedStatement.setBlob(index.getAndIncrement(), (Blob) object);
            }
            else if (object instanceof Boolean) {
                preparedStatement.setBoolean(index.getAndIncrement(), (Boolean) object);
            }
            else if (object instanceof Byte) {
                preparedStatement.setByte(index.getAndIncrement(), (Byte) object);
            }
            else if (object instanceof byte[]) {
                preparedStatement.setBytes(index.getAndIncrement(), (byte[]) object);
            }
            else if (object instanceof Clob) {
                preparedStatement.setClob(index.getAndIncrement(), (Clob) object);
            }
            else if (object instanceof Double) {
                preparedStatement.setDouble(index.getAndIncrement(), (Double) object);
            }
            else if (object instanceof Float) {
                preparedStatement.setFloat(index.getAndIncrement(), (Float) object);
            }
            else if (object instanceof Integer) {
                preparedStatement.setInt(index.getAndIncrement(), (Integer) object);
            }
            else if (object instanceof List) {
                for (Object listObject : (List) object) {
                    setPreparedStatementParameter(preparedStatement, preparedStatementParameters, listObject, index);
                }
            }
            else if (object instanceof Long) {
                preparedStatement.setLong(index.getAndIncrement(), (Long) object);
            }
            else if (object instanceof NClob) {
                preparedStatement.setNClob(index.getAndIncrement(), (NClob) object);
            }
            else if (object instanceof Ref) {
                preparedStatement.setRef(index.getAndIncrement(), (Ref) object);
            }
            else if (object instanceof RowId) {
                preparedStatement.setRowId(index.getAndIncrement(), (RowId) object);
            }
            else if (object instanceof SQLXML) {
                preparedStatement.setSQLXML(index.getAndIncrement(), (SQLXML) object);
            }
            else if (object instanceof Short) {
                preparedStatement.setShort(index.getAndIncrement(), (Short) object);
            }
            else if (object instanceof String) {
                preparedStatement.setString(index.getAndIncrement(), (String) object);
            }
            else if (object instanceof Time) {
                preparedStatement.setTime(index.getAndIncrement(), (Time) object);
            }
            else if (object instanceof java.sql.Timestamp) {
                preparedStatement.setTimestamp(index.getAndIncrement(), (java.sql.Timestamp) object);
            }
            else if (object instanceof java.sql.Date) {
                preparedStatement.setDate(index.getAndIncrement(), (java.sql.Date) object);
            }
            else if (object instanceof java.util.Date) {
                java.util.Date tempDate = (java.util.Date) object;
                java.sql.Date dateSql = new java.sql.Date(tempDate.getTime());
                preparedStatement.setDate(index.getAndIncrement(), dateSql);
            }
            else {
                if (object instanceof Object) {}
                else {
                    logger.warn("Setting PreparedStatement parameter to 'object' type when object is not an object type");
                }
                
                preparedStatement.setObject(index.getAndIncrement(), object);
            }
            
            return index.get();
        }
        catch (Exception e) {
            logger.error(e.toString() + System.lineSeparator() + StackTrace.getStringFromStackTrace(e));
            return -1;
        }  
        
    }

    public static String getStringFromClob(Clob clob) {
        return getStringFromClob(clob, 8192);
    }
    
    public static String getStringFromClob(Clob clob, int bufferSize) {
        
        if (clob == null) {
            return null;
        }
        
        StringBuilder string = new StringBuilder();
        
        BufferedReader bufferedReader = null;
        
        try {
            bufferedReader = new BufferedReader(clob.getCharacterStream());

            int charactersReadCount = 0;
            char[] buffer = new char[bufferSize];

            while ((charactersReadCount = bufferedReader.read(buffer)) > 0) {
                string.append(buffer, 0, charactersReadCount);
            }
        }
        catch (Exception e) {
            logger.error(e.toString() + System.lineSeparator() + StackTrace.getStringFromStackTrace(e));
        }
        finally {
            try {
                if (bufferedReader != null) {
                    bufferedReader.close();
                }
            }
            catch (Exception e) {
                logger.error(e.toString() + System.lineSeparator() + StackTrace.getStringFromStackTrace(e));
            }
        }

        return string.toString();
    }
    
    private static List<Object> getParametersAsListOfObjects(Object... parameters) {
        
        List<Object> parametersList;
        
        try {
            parametersList = new ArrayList<>(Arrays.asList(parameters));
        }
        catch (Exception e) {
            logger.error(e.toString() + System.lineSeparator() + StackTrace.getStringFromStackTrace(e));
            parametersList = new ArrayList<>();
        }
        
        return parametersList;
    }
    
}