org.apache.sqoop.repository.common.CommonRepositoryHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.sqoop.repository.common.CommonRepositoryHandler.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.sqoop.repository.common;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.sqoop.common.Direction;
import org.apache.sqoop.common.DirectionError;
import org.apache.sqoop.common.SqoopException;
import org.apache.sqoop.common.SupportedDirections;
import org.apache.sqoop.driver.Driver;
import org.apache.sqoop.error.code.CommonRepositoryError;
import org.apache.sqoop.model.InputEditable;
import org.apache.sqoop.model.MConfigUpdateEntityType;
import org.apache.sqoop.model.MLongInput;
import org.apache.sqoop.model.SubmissionError;
import org.apache.sqoop.model.MBooleanInput;
import org.apache.sqoop.model.MConfig;
import org.apache.sqoop.model.MConfigType;
import org.apache.sqoop.model.MConfigurableType;
import org.apache.sqoop.model.MConnector;
import org.apache.sqoop.model.MDriver;
import org.apache.sqoop.model.MDriverConfig;
import org.apache.sqoop.model.MEnumInput;
import org.apache.sqoop.model.MFromConfig;
import org.apache.sqoop.model.MInput;
import org.apache.sqoop.model.MInputType;
import org.apache.sqoop.model.MIntegerInput;
import org.apache.sqoop.model.MJob;
import org.apache.sqoop.model.MLink;
import org.apache.sqoop.model.MLinkConfig;
import org.apache.sqoop.model.MMapInput;
import org.apache.sqoop.model.MStringInput;
import org.apache.sqoop.model.MSubmission;
import org.apache.sqoop.model.MToConfig;
import org.apache.sqoop.repository.JdbcRepositoryHandler;
import org.apache.sqoop.submission.SubmissionStatus;
import org.apache.sqoop.submission.counter.Counter;
import org.apache.sqoop.submission.counter.CounterGroup;
import org.apache.sqoop.submission.counter.Counters;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * Set of methods required from each JDBC based repository.
 */
public abstract class CommonRepositoryHandler extends JdbcRepositoryHandler {

    private static final Logger LOG = Logger.getLogger(CommonRepositoryHandler.class);

    protected CommonRepositoryInsertUpdateDeleteSelectQuery crudQueries;

    public CommonRepositoryHandler() {
        crudQueries = new CommonRepositoryInsertUpdateDeleteSelectQuery();
    }

    /**
     * Name to be used during logging
     *
     * @return String
     */
    public abstract String name();

    /**
     * {@inheritDoc}
     */
    @Override
    public MConnector findConnector(String shortName, Connection conn) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("Looking up connector: " + shortName);
        }
        MConnector mc = null;
        PreparedStatement connectorFetchStmt = null;
        try {
            connectorFetchStmt = conn.prepareStatement(crudQueries.getStmtSelectFromConfigurable());
            connectorFetchStmt.setString(1, shortName);

            List<MConnector> connectors = loadConnectors(connectorFetchStmt, conn);

            if (connectors.size() == 0) {
                LOG.debug("No connector found by name: " + shortName);
                return null;
            } else if (connectors.size() == 1) {
                LOG.debug("Looking up connector: " + shortName + ", found: " + mc);
                return connectors.get(0);
            } else {
                throw new SqoopException(CommonRepositoryError.COMMON_0002, shortName);
            }

        } catch (SQLException ex) {
            logException(ex, shortName);
            throw new SqoopException(CommonRepositoryError.COMMON_0001, shortName, ex);
        } finally {
            closeStatements(connectorFetchStmt);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<MConnector> findConnectors(Connection conn) {
        PreparedStatement stmt = null;
        try {
            stmt = conn.prepareStatement(crudQueries.getStmtSelectConfigurableAllForType());
            stmt.setString(1, MConfigurableType.CONNECTOR.name());

            return loadConnectors(stmt, conn);
        } catch (SQLException ex) {
            logException(ex);
            throw new SqoopException(CommonRepositoryError.COMMON_0041, ex);
        } finally {
            closeStatements(stmt);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void registerConnector(MConnector mc, Connection conn) {
        if (mc.hasPersistenceId()) {
            throw new SqoopException(CommonRepositoryError.COMMON_0008, mc.getUniqueName());
        }
        mc.setPersistenceId(insertAndGetConnectorId(mc, conn));
        insertConfigsForConnector(mc, conn);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<MJob> findJobsForConnector(long connectorId, Connection conn) {
        PreparedStatement stmt = null;
        try {
            stmt = conn.prepareStatement(crudQueries.getStmtSelectAllJobsForConnectorConfigurable());
            stmt.setLong(1, connectorId);
            stmt.setLong(2, connectorId);
            return loadJobs(stmt, conn);

        } catch (SQLException ex) {
            logException(ex, connectorId);
            throw new SqoopException(CommonRepositoryError.COMMON_0028, ex);
        } finally {
            closeStatements(stmt);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void upgradeConnectorAndConfigs(MConnector mConnector, Connection conn) {
        updateConnectorAndDeleteConfigs(mConnector, conn);
        insertConfigsForConnector(mConnector, conn);
    }

    private void updateConnectorAndDeleteConfigs(MConnector mConnector, Connection conn) {
        PreparedStatement updateConnectorStatement = null;
        PreparedStatement deleteConfig = null;
        PreparedStatement deleteConfigDirection = null;
        PreparedStatement deleteInput = null;
        PreparedStatement deleteInputRelation = null;

        try {
            updateConnectorStatement = conn.prepareStatement(crudQueries.getStmtUpdateConfigurable());
            deleteInputRelation = conn.prepareStatement(
                    CommonRepositoryInsertUpdateDeleteSelectQuery.STMT_DELETE_INPUT_RELATIONS_FOR_INPUT);
            deleteInput = conn.prepareStatement(crudQueries.getStmtDeleteInputsForConfigurable());
            deleteConfigDirection = conn.prepareStatement(crudQueries.getStmtDeleteDirectionsForConfigurable());
            deleteConfig = conn.prepareStatement(crudQueries.getStmtDeleteConfigsForConfigurable());

            updateConnectorStatement.setString(1, mConnector.getUniqueName());
            updateConnectorStatement.setString(2, mConnector.getClassName());
            updateConnectorStatement.setString(3, mConnector.getVersion());
            updateConnectorStatement.setString(4, mConnector.getType().name());
            updateConnectorStatement.setLong(5, mConnector.getPersistenceId());

            if (updateConnectorStatement.executeUpdate() != 1) {
                throw new SqoopException(CommonRepositoryError.COMMON_0035);
            }
            deleteInputRelation.setLong(1, mConnector.getPersistenceId());
            deleteInput.setLong(1, mConnector.getPersistenceId());
            deleteConfigDirection.setLong(1, mConnector.getPersistenceId());
            deleteConfig.setLong(1, mConnector.getPersistenceId());
            deleteInputRelation.executeUpdate();
            deleteInput.executeUpdate();
            deleteConfigDirection.executeUpdate();
            deleteConfig.executeUpdate();

        } catch (SQLException e) {
            logException(e, mConnector);
            throw new SqoopException(CommonRepositoryError.COMMON_0035, e);
        } finally {
            closeStatements(updateConnectorStatement, deleteConfig, deleteConfigDirection, deleteInput);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void upgradeDriverAndConfigs(MDriver mDriver, Connection conn) {
        updateDriverAndDeleteConfigs(mDriver, conn);
        insertConfigsForDriver(mDriver, conn);
    }

    private void updateDriverAndDeleteConfigs(MDriver mDriver, Connection conn) {
        PreparedStatement updateDriverStatement = null;
        PreparedStatement deleteConfig = null;
        PreparedStatement deleteInput = null;
        PreparedStatement deleteInputRelation = null;
        try {
            updateDriverStatement = conn.prepareStatement(crudQueries.getStmtUpdateConfigurable());
            deleteInputRelation = conn.prepareStatement(
                    CommonRepositoryInsertUpdateDeleteSelectQuery.STMT_DELETE_INPUT_RELATIONS_FOR_INPUT);
            deleteInput = conn.prepareStatement(crudQueries.getStmtDeleteInputsForConfigurable());
            deleteConfig = conn.prepareStatement(crudQueries.getStmtDeleteConfigsForConfigurable());
            updateDriverStatement.setString(1, mDriver.getUniqueName());
            updateDriverStatement.setString(2, Driver.getClassName());
            updateDriverStatement.setString(3, mDriver.getVersion());
            updateDriverStatement.setString(4, mDriver.getType().name());
            updateDriverStatement.setLong(5, mDriver.getPersistenceId());

            if (updateDriverStatement.executeUpdate() != 1) {
                throw new SqoopException(CommonRepositoryError.COMMON_0035);
            }
            deleteInputRelation.setLong(1, mDriver.getPersistenceId());
            deleteInput.setLong(1, mDriver.getPersistenceId());
            deleteConfig.setLong(1, mDriver.getPersistenceId());
            deleteInputRelation.executeUpdate();
            deleteInput.executeUpdate();
            deleteConfig.executeUpdate();

        } catch (SQLException e) {
            logException(e, mDriver);
            throw new SqoopException(CommonRepositoryError.COMMON_0040, e);
        } finally {
            closeStatements(updateDriverStatement, deleteConfig, deleteInput);
        }
    }

    /**
     * Helper method to insert the configs from the  into the
     * repository.
     *
     * @param mDriver The driver instance to use to upgrade.
     * @param conn    JDBC link to use for updating the configs
     */
    private void insertConfigsForDriver(MDriver mDriver, Connection conn) {
        long driverId = mDriver.getPersistenceId();
        PreparedStatement baseConfigStmt = null;
        PreparedStatement baseInputStmt = null;
        try {
            baseConfigStmt = conn.prepareStatement(crudQueries.getStmtInsertIntoConfig(),
                    Statement.RETURN_GENERATED_KEYS);

            baseInputStmt = conn.prepareStatement(crudQueries.getStmtInsertIntoInput(),
                    Statement.RETURN_GENERATED_KEYS);

            // Register a driver config as a job type with no direction
            registerConfigs(driverId, null, mDriver.getDriverConfig().getConfigs(), MConfigType.JOB.name(),
                    baseConfigStmt, baseInputStmt, conn);

        } catch (SQLException ex) {
            throw new SqoopException(CommonRepositoryError.COMMON_0011, mDriver.toString(), ex);
        } finally {
            closeStatements(baseConfigStmt, baseInputStmt);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public MDriver findDriver(String shortName, Connection conn) {
        LOG.debug("Looking up Driver and config ");
        PreparedStatement driverFetchStmt = null;
        PreparedStatement driverConfigFetchStmt = null;
        PreparedStatement driverConfigInputFetchStmt = null;

        MDriver mDriver;
        try {
            driverFetchStmt = conn.prepareStatement(crudQueries.getStmtSelectFromConfigurable());
            driverFetchStmt.setString(1, shortName);

            ResultSet rsDriverSet = driverFetchStmt.executeQuery();
            if (!rsDriverSet.next()) {
                return null;
            }
            Long driverId = rsDriverSet.getLong(1);
            String driverVersion = rsDriverSet.getString(4);

            driverConfigFetchStmt = conn.prepareStatement(crudQueries.getStmtSelectConfigForConfigurable());
            driverConfigFetchStmt.setLong(1, driverId);

            driverConfigInputFetchStmt = conn.prepareStatement(crudQueries.getStmtSelectInput());
            List<MConfig> driverConfigs = new ArrayList<MConfig>();
            loadDriverConfigs(driverConfigs, driverConfigFetchStmt, driverConfigInputFetchStmt, 1, conn);

            mDriver = new MDriver(new MDriverConfig(driverConfigs), driverVersion);
            mDriver.setPersistenceId(driverId);

        } catch (SQLException ex) {
            throw new SqoopException(CommonRepositoryError.COMMON_0001, "Driver", ex);
        } finally {
            if (driverConfigFetchStmt != null) {
                try {
                    driverConfigFetchStmt.close();
                } catch (SQLException ex) {
                    LOG.error("Unable to close driver config fetch statement", ex);
                }
            }
            if (driverConfigInputFetchStmt != null) {
                try {
                    driverConfigInputFetchStmt.close();
                } catch (SQLException ex) {
                    LOG.error("Unable to close driver input fetch statement", ex);
                }
            }
            if (driverFetchStmt != null) {
                try {
                    driverFetchStmt.close();
                } catch (SQLException ex) {
                    LOG.error("Unable to close driver fetch statement", ex);
                }
            }
        }

        LOG.debug("Looked up Driver and config");
        return mDriver;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void registerDriver(MDriver mDriver, Connection conn) {
        if (mDriver.hasPersistenceId()) {
            throw new SqoopException(CommonRepositoryError.COMMON_0008, mDriver.getUniqueName());
        }
        mDriver.setPersistenceId(insertAndGetDriverId(mDriver, conn));
        insertConfigsForDriver(mDriver, conn);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void createLink(MLink link, Connection conn) {
        PreparedStatement stmt = null;
        int result;
        try {
            stmt = conn.prepareStatement(crudQueries.getStmtInsertLink(), Statement.RETURN_GENERATED_KEYS);
            stmt.setString(1, link.getName());
            stmt.setLong(2, link.getConnectorId());
            stmt.setBoolean(3, link.getEnabled());
            stmt.setString(4, link.getCreationUser());
            stmt.setTimestamp(5, new Timestamp(link.getCreationDate().getTime()));
            stmt.setString(6, link.getLastUpdateUser());
            stmt.setTimestamp(7, new Timestamp(link.getLastUpdateDate().getTime()));

            result = stmt.executeUpdate();
            if (result != 1) {
                throw new SqoopException(CommonRepositoryError.COMMON_0009, Integer.toString(result));
            }

            ResultSet rsetConnectionId = stmt.getGeneratedKeys();

            if (!rsetConnectionId.next()) {
                throw new SqoopException(CommonRepositoryError.COMMON_0010);
            }

            long connectionId = rsetConnectionId.getLong(1);

            createInputValues(crudQueries.getStmtInsertLinkInput(), connectionId,
                    link.getConnectorLinkConfig().getConfigs(), conn);
            link.setPersistenceId(connectionId);

        } catch (SQLException ex) {
            logException(ex, link);
            throw new SqoopException(CommonRepositoryError.COMMON_0016, ex);
        } finally {
            closeStatements(stmt);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void updateLink(MLink link, Connection conn) {
        PreparedStatement deleteStmt = null;
        PreparedStatement updateStmt = null;
        try {
            // Firstly remove old values
            deleteStmt = conn.prepareStatement(crudQueries.getStmtDeleteLinkInput());
            deleteStmt.setLong(1, link.getPersistenceId());
            deleteStmt.executeUpdate();

            // Update LINK_CONFIG table
            updateStmt = conn.prepareStatement(crudQueries.getStmtUpdateLink());
            updateStmt.setString(1, link.getName());
            updateStmt.setString(2, link.getLastUpdateUser());
            updateStmt.setTimestamp(3, new Timestamp(new Date().getTime()));

            updateStmt.setLong(4, link.getPersistenceId());
            updateStmt.executeUpdate();

            // And reinsert new values
            createInputValues(crudQueries.getStmtInsertLinkInput(), link.getPersistenceId(),
                    link.getConnectorLinkConfig().getConfigs(), conn);

        } catch (SQLException ex) {
            logException(ex, link);
            throw new SqoopException(CommonRepositoryError.COMMON_0018, ex);
        } finally {
            closeStatements(deleteStmt, updateStmt);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean existsLink(long linkId, Connection conn) {
        PreparedStatement stmt = null;
        ResultSet rs = null;
        try {
            stmt = conn.prepareStatement(crudQueries.getStmtSelectLinkCheckById());
            stmt.setLong(1, linkId);
            rs = stmt.executeQuery();

            // Should be always valid in query with count
            rs.next();

            return rs.getLong(1) == 1;
        } catch (SQLException ex) {
            logException(ex, linkId);
            throw new SqoopException(CommonRepositoryError.COMMON_0022, ex);
        } finally {
            closeResultSets(rs);
            closeStatements(stmt);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean inUseLink(long linkId, Connection conn) {
        PreparedStatement stmt = null;
        ResultSet rs = null;

        try {
            stmt = conn.prepareStatement(crudQueries.getStmtSelectJobsForLinkCheck());
            stmt.setLong(1, linkId);
            rs = stmt.executeQuery();

            // Should be always valid in case of count(*) query
            rs.next();

            return rs.getLong(1) != 0;

        } catch (SQLException e) {
            logException(e, linkId);
            throw new SqoopException(CommonRepositoryError.COMMON_0029, e);
        } finally {
            closeResultSets(rs);
            closeStatements(stmt);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void enableLink(long linkId, boolean enabled, Connection conn) {
        PreparedStatement enableConn = null;

        try {
            enableConn = conn.prepareStatement(crudQueries.getStmtEnableLink());
            enableConn.setBoolean(1, enabled);
            enableConn.setLong(2, linkId);
            enableConn.executeUpdate();
        } catch (SQLException ex) {
            logException(ex, linkId);
            throw new SqoopException(CommonRepositoryError.COMMON_0038, ex);
        } finally {
            closeStatements(enableConn);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void deleteLink(long linkId, Connection conn) {
        PreparedStatement dltConn = null;

        try {
            deleteLinkInputs(linkId, conn);
            dltConn = conn.prepareStatement(crudQueries.getStmtDeleteLink());
            dltConn.setLong(1, linkId);
            dltConn.executeUpdate();
        } catch (SQLException ex) {
            logException(ex, linkId);
            throw new SqoopException(CommonRepositoryError.COMMON_0019, ex);
        } finally {
            closeStatements(dltConn);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void deleteLinkInputs(long id, Connection conn) {
        PreparedStatement dltConnInput = null;
        try {
            dltConnInput = conn.prepareStatement(crudQueries.getStmtDeleteLinkInput());
            dltConnInput.setLong(1, id);
            dltConnInput.executeUpdate();
        } catch (SQLException ex) {
            logException(ex, id);
            throw new SqoopException(CommonRepositoryError.COMMON_0019, ex);
        } finally {
            closeStatements(dltConnInput);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public MLink findLink(long linkId, Connection conn) {
        PreparedStatement linkFetchStmt = null;
        try {
            linkFetchStmt = conn.prepareStatement(crudQueries.getStmtSelectLinkSingle());
            linkFetchStmt.setLong(1, linkId);

            List<MLink> links = loadLinks(linkFetchStmt, conn);

            if (links.size() != 1) {
                throw new SqoopException(CommonRepositoryError.COMMON_0021,
                        "Couldn't find" + " link with id " + linkId);
            }

            // Return the first and only one link object with the given id
            return links.get(0);

        } catch (SQLException ex) {
            logException(ex, linkId);
            throw new SqoopException(CommonRepositoryError.COMMON_0020, ex);
        } finally {
            closeStatements(linkFetchStmt);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public MLink findLink(String linkName, Connection conn) {
        PreparedStatement linkFetchStmt = null;
        try {
            linkFetchStmt = conn.prepareStatement(crudQueries.getStmtSelectLinkSingleByName());
            linkFetchStmt.setString(1, linkName);

            List<MLink> links = loadLinks(linkFetchStmt, conn);

            if (links.size() != 1) {
                return null;
            }

            // Return the first and only one link object with the given name
            return links.get(0);

        } catch (SQLException ex) {
            logException(ex, linkName);
            throw new SqoopException(CommonRepositoryError.COMMON_0020, ex);
        } finally {
            closeStatements(linkFetchStmt);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<MLink> findLinks(Connection conn) {
        PreparedStatement linksFetchStmt = null;
        try {
            linksFetchStmt = conn.prepareStatement(crudQueries.getStmtSelectLinkAll());

            return loadLinks(linksFetchStmt, conn);

        } catch (SQLException ex) {
            logException(ex);
            throw new SqoopException(CommonRepositoryError.COMMON_0020, ex);
        } finally {
            closeStatements(linksFetchStmt);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<MLink> findLinksForConnector(long connectorId, Connection conn) {
        PreparedStatement linkByConnectorFetchStmt = null;
        try {
            linkByConnectorFetchStmt = conn
                    .prepareStatement(crudQueries.getStmtSelectLinkForConnectorConfigurable());
            linkByConnectorFetchStmt.setLong(1, connectorId);
            return loadLinks(linkByConnectorFetchStmt, conn);
        } catch (SQLException ex) {
            logException(ex, connectorId);
            throw new SqoopException(CommonRepositoryError.COMMON_0020, ex);
        } finally {
            closeStatements(linkByConnectorFetchStmt);
        }
    }

    /**
     * {@inheritDoc}
     */
    public void createJob(MJob job, Connection conn) {
        PreparedStatement stmt = null;
        int result;
        try {
            stmt = conn.prepareStatement(crudQueries.getStmtInsertJob(), Statement.RETURN_GENERATED_KEYS);
            stmt.setString(1, job.getName());
            stmt.setLong(2, job.getFromLinkId());
            stmt.setLong(3, job.getToLinkId());
            stmt.setBoolean(4, job.getEnabled());
            stmt.setString(5, job.getCreationUser());
            stmt.setTimestamp(6, new Timestamp(job.getCreationDate().getTime()));
            stmt.setString(7, job.getLastUpdateUser());
            stmt.setTimestamp(8, new Timestamp(job.getLastUpdateDate().getTime()));

            result = stmt.executeUpdate();
            if (result != 1) {
                throw new SqoopException(CommonRepositoryError.COMMON_0009, Integer.toString(result));
            }

            ResultSet rsetJobId = stmt.getGeneratedKeys();

            if (!rsetJobId.next()) {
                throw new SqoopException(CommonRepositoryError.COMMON_0010);
            }

            long jobId = rsetJobId.getLong(1);

            // from config for the job
            createInputValues(crudQueries.getStmtInsertJobInput(), jobId, job.getFromJobConfig().getConfigs(),
                    conn);
            // to config for the job
            createInputValues(crudQueries.getStmtInsertJobInput(), jobId, job.getToJobConfig().getConfigs(), conn);
            // driver config per job
            createInputValues(crudQueries.getStmtInsertJobInput(), jobId, job.getDriverConfig().getConfigs(), conn);

            job.setPersistenceId(jobId);

        } catch (SQLException ex) {
            logException(ex, job);
            throw new SqoopException(CommonRepositoryError.COMMON_0023, ex);
        } finally {
            closeStatements(stmt);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void updateJob(MJob job, Connection conn) {
        PreparedStatement deleteStmt = null;
        PreparedStatement updateStmt = null;
        try {
            // Firstly remove old values
            deleteStmt = conn.prepareStatement(crudQueries.getStmtDeleteJobInput());
            deleteStmt.setLong(1, job.getPersistenceId());
            deleteStmt.executeUpdate();

            // Update job table
            updateStmt = conn.prepareStatement(crudQueries.getStmtUpdateJob());
            updateStmt.setString(1, job.getName());
            updateStmt.setString(2, job.getLastUpdateUser());
            updateStmt.setTimestamp(3, new Timestamp(new Date().getTime()));

            updateStmt.setLong(4, job.getPersistenceId());
            updateStmt.executeUpdate();

            // And reinsert new values
            createInputValues(crudQueries.getStmtInsertJobInput(), job.getPersistenceId(),
                    job.getFromJobConfig().getConfigs(), conn);
            createInputValues(crudQueries.getStmtInsertJobInput(), job.getPersistenceId(),
                    job.getToJobConfig().getConfigs(), conn);
            createInputValues(crudQueries.getStmtInsertJobInput(), job.getPersistenceId(),
                    job.getDriverConfig().getConfigs(), conn);

        } catch (SQLException ex) {
            logException(ex, job);
            throw new SqoopException(CommonRepositoryError.COMMON_0024, ex);
        } finally {
            closeStatements(deleteStmt, updateStmt);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean existsJob(long jobId, Connection conn) {
        PreparedStatement stmt = null;
        ResultSet rs = null;
        try {
            stmt = conn.prepareStatement(crudQueries.getStmtSelectJobCheckById());
            stmt.setLong(1, jobId);
            rs = stmt.executeQuery();

            // Should be always valid in query with count
            rs.next();

            return rs.getLong(1) == 1;
        } catch (SQLException ex) {
            logException(ex, jobId);
            throw new SqoopException(CommonRepositoryError.COMMON_0026, ex);
        } finally {
            closeResultSets(rs);
            closeStatements(stmt);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean inUseJob(long jobId, Connection conn) {
        MSubmission submission = findLastSubmissionForJob(jobId, conn);

        // We have no submissions and thus job can't be in use
        if (submission == null) {
            return false;
        }

        // We can't remove running job
        if (submission.getStatus().isRunning()) {
            return true;
        }

        return false;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void enableJob(long jobId, boolean enabled, Connection conn) {
        PreparedStatement enableConn = null;

        try {
            enableConn = conn.prepareStatement(crudQueries.getStmtEnableJob());
            enableConn.setBoolean(1, enabled);
            enableConn.setLong(2, jobId);
            enableConn.executeUpdate();
        } catch (SQLException ex) {
            logException(ex, jobId);
            throw new SqoopException(CommonRepositoryError.COMMON_0039, ex);
        } finally {
            closeStatements(enableConn);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void deleteJobInputs(long id, Connection conn) {
        PreparedStatement dltInput = null;
        try {
            dltInput = conn.prepareStatement(crudQueries.getStmtDeleteJobInput());
            dltInput.setLong(1, id);
            dltInput.executeUpdate();
        } catch (SQLException ex) {
            logException(ex, id);
            throw new SqoopException(CommonRepositoryError.COMMON_0025, ex);
        } finally {
            closeStatements(dltInput);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void deleteJob(long jobId, Connection conn) {
        PreparedStatement dlt = null;
        try {
            deleteJobInputs(jobId, conn);
            dlt = conn.prepareStatement(crudQueries.getStmtDeleteJob());
            dlt.setLong(1, jobId);
            dlt.executeUpdate();
        } catch (SQLException ex) {
            logException(ex, jobId);
            throw new SqoopException(CommonRepositoryError.COMMON_0025, ex);
        } finally {
            closeStatements(dlt);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public MJob findJob(long jobId, Connection conn) {
        PreparedStatement stmt = null;
        try {
            stmt = conn.prepareStatement(crudQueries.getStmtSelectJobSingleById());
            stmt.setLong(1, jobId);

            List<MJob> jobs = loadJobs(stmt, conn);

            if (jobs.size() != 1) {
                throw new SqoopException(CommonRepositoryError.COMMON_0027,
                        "Couldn't find" + " job with id " + jobId);
            }

            // Return the first and only one link object
            return jobs.get(0);

        } catch (SQLException ex) {
            logException(ex, jobId);
            throw new SqoopException(CommonRepositoryError.COMMON_0028, ex);
        } finally {
            closeStatements(stmt);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public MJob findJob(String name, Connection conn) {
        PreparedStatement stmt = null;
        try {
            stmt = conn.prepareStatement(crudQueries.getStmtSelectJobSingleByName());
            stmt.setString(1, name);

            List<MJob> jobs = loadJobs(stmt, conn);

            if (jobs.size() != 1) {
                return null;
            }

            // Return the first and only one link object
            return jobs.get(0);

        } catch (SQLException ex) {
            logException(ex, name);
            throw new SqoopException(CommonRepositoryError.COMMON_0028, ex);
        } finally {
            closeStatements(stmt);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<MJob> findJobs(Connection conn) {
        PreparedStatement stmt = null;
        try {
            stmt = conn.prepareStatement(crudQueries.getStmtSelectJobAll());
            return loadJobs(stmt, conn);
        } catch (SQLException ex) {
            logException(ex);
            throw new SqoopException(CommonRepositoryError.COMMON_0028, ex);
        } finally {
            closeStatements(stmt);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void createSubmission(MSubmission submission, Connection conn) {
        PreparedStatement stmt = null;
        int result;
        try {
            stmt = conn.prepareStatement(crudQueries.getStmtInsertSubmission(), Statement.RETURN_GENERATED_KEYS);
            stmt.setLong(1, submission.getJobId());
            stmt.setString(2, submission.getStatus().name());
            stmt.setString(3, submission.getCreationUser());
            stmt.setTimestamp(4, new Timestamp(submission.getCreationDate().getTime()));
            stmt.setString(5, submission.getLastUpdateUser());
            stmt.setTimestamp(6, new Timestamp(submission.getLastUpdateDate().getTime()));
            stmt.setString(7, submission.getExternalJobId());
            stmt.setString(8, submission.getExternalLink());
            stmt.setString(9, submission.getError().getErrorSummary());
            stmt.setString(10, submission.getError().getErrorDetails());

            result = stmt.executeUpdate();
            if (result != 1) {
                throw new SqoopException(CommonRepositoryError.COMMON_0009, Integer.toString(result));
            }

            ResultSet rsetSubmissionId = stmt.getGeneratedKeys();

            if (!rsetSubmissionId.next()) {
                throw new SqoopException(CommonRepositoryError.COMMON_0010);
            }

            long submissionId = rsetSubmissionId.getLong(1);

            if (submission.getCounters() != null) {
                createSubmissionCounters(submissionId, submission.getCounters(), conn);
            }

            // Save created persistence id
            submission.setPersistenceId(submissionId);

        } catch (SQLException ex) {
            logException(ex, submission);
            throw new SqoopException(CommonRepositoryError.COMMON_0031, ex);
        } finally {
            closeStatements(stmt);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean existsSubmission(long submissionId, Connection conn) {
        PreparedStatement stmt = null;
        ResultSet rs = null;
        try {
            stmt = conn.prepareStatement(crudQueries.getStmtSelectSubmissionCheck());
            stmt.setLong(1, submissionId);
            rs = stmt.executeQuery();

            // Should be always valid in query with count
            rs.next();

            return rs.getLong(1) == 1;
        } catch (SQLException ex) {
            logException(ex, submissionId);
            throw new SqoopException(CommonRepositoryError.COMMON_0030, ex);
        } finally {
            closeResultSets(rs);
            closeStatements(stmt);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void updateSubmission(MSubmission submission, Connection conn) {
        PreparedStatement stmt = null;
        PreparedStatement deleteStmt = null;
        try {
            //  Update properties in main table
            stmt = conn.prepareStatement(crudQueries.getStmtUpdateSubmission());
            stmt.setString(1, submission.getStatus().name());
            stmt.setString(2, submission.getLastUpdateUser());
            stmt.setTimestamp(3, new Timestamp(submission.getLastUpdateDate().getTime()));
            stmt.setString(4, submission.getError().getErrorSummary());
            stmt.setString(5, submission.getError().getErrorDetails());

            stmt.setLong(6, submission.getPersistenceId());
            stmt.executeUpdate();

            // Delete previous counters
            deleteStmt = conn.prepareStatement(crudQueries.getStmtDeleteCounterSubmission());
            deleteStmt.setLong(1, submission.getPersistenceId());
            deleteStmt.executeUpdate();

            // Reinsert new counters if needed
            if (submission.getCounters() != null) {
                createSubmissionCounters(submission.getPersistenceId(), submission.getCounters(), conn);
            }

        } catch (SQLException ex) {
            logException(ex, submission);
            throw new SqoopException(CommonRepositoryError.COMMON_0032, ex);
        } finally {
            closeStatements(stmt, deleteStmt);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void purgeSubmissions(Date threshold, Connection conn) {
        PreparedStatement stmt = null;
        try {
            stmt = conn.prepareStatement(crudQueries.getStmtPurgeSubmissions());
            stmt.setTimestamp(1, new Timestamp(threshold.getTime()));
            stmt.executeUpdate();

        } catch (SQLException ex) {
            logException(ex, threshold);
            throw new SqoopException(CommonRepositoryError.COMMON_0033, ex);
        } finally {
            closeStatements(stmt);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<MSubmission> findUnfinishedSubmissions(Connection conn) {
        List<MSubmission> submissions = new LinkedList<MSubmission>();
        PreparedStatement stmt = null;
        ResultSet rs = null;
        try {
            stmt = conn.prepareStatement(crudQueries.getStmtSelectSubmissionUnfinished());

            for (SubmissionStatus status : SubmissionStatus.unfinished()) {
                stmt.setString(1, status.name());
                rs = stmt.executeQuery();

                while (rs.next()) {
                    submissions.add(loadSubmission(rs, conn));
                }

                rs.close();
                rs = null;
            }
        } catch (SQLException ex) {
            logException(ex);
            throw new SqoopException(CommonRepositoryError.COMMON_0034, ex);
        } finally {
            closeResultSets(rs);
            closeStatements(stmt);
        }

        return submissions;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<MSubmission> findSubmissions(Connection conn) {
        List<MSubmission> submissions = new LinkedList<MSubmission>();
        PreparedStatement stmt = null;
        ResultSet rs = null;
        try {
            stmt = conn.prepareStatement(crudQueries.getStmtSelectSubmissions());
            rs = stmt.executeQuery();

            while (rs.next()) {
                submissions.add(loadSubmission(rs, conn));
            }

            rs.close();
            rs = null;
        } catch (SQLException ex) {
            logException(ex);
            throw new SqoopException(CommonRepositoryError.COMMON_0036, ex);
        } finally {
            closeResultSets(rs);
            closeStatements(stmt);
        }

        return submissions;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<MSubmission> findSubmissionsForJob(long jobId, Connection conn) {
        List<MSubmission> submissions = new LinkedList<MSubmission>();
        PreparedStatement stmt = null;
        ResultSet rs = null;
        try {
            stmt = conn.prepareStatement(crudQueries.getStmtSelectSubmissionsForJob());
            stmt.setLong(1, jobId);
            rs = stmt.executeQuery();

            while (rs.next()) {
                submissions.add(loadSubmission(rs, conn));
            }

            rs.close();
            rs = null;
        } catch (SQLException ex) {
            logException(ex);
            throw new SqoopException(CommonRepositoryError.COMMON_0037, ex);
        } finally {
            closeResultSets(rs);
            closeStatements(stmt);
        }

        return submissions;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public MSubmission findLastSubmissionForJob(long jobId, Connection conn) {
        PreparedStatement stmt = null;
        ResultSet rs = null;
        try {
            stmt = conn.prepareStatement(crudQueries.getStmtSelectSubmissionsForJob());
            stmt.setLong(1, jobId);
            stmt.setMaxRows(1);
            rs = stmt.executeQuery();

            if (!rs.next()) {
                return null;
            }

            return loadSubmission(rs, conn);
        } catch (SQLException ex) {
            logException(ex, jobId);
            throw new SqoopException(CommonRepositoryError.COMMON_0037, ex);
        } finally {
            closeResultSets(rs);
            closeStatements(stmt);
        }
    }

    private void insertConnectorDirection(Long connectorId, Direction direction, Connection conn)
            throws SQLException {
        PreparedStatement stmt = null;

        try {
            stmt = conn.prepareStatement(crudQueries.getStmtInsertSqConnectorDirections());
            stmt.setLong(1, connectorId);
            stmt.setLong(2, getDirection(direction, conn));

            if (stmt.executeUpdate() != 1) {
                throw new SqoopException(CommonRepositoryError.COMMON_0043);
            }
        } finally {
            closeStatements(stmt);
        }
    }

    private void insertConnectorDirections(Long connectorId, SupportedDirections directions, Connection conn)
            throws SQLException {
        if (directions.isDirectionSupported(Direction.FROM)) {
            insertConnectorDirection(connectorId, Direction.FROM, conn);
        }

        if (directions.isDirectionSupported(Direction.TO)) {
            insertConnectorDirection(connectorId, Direction.TO, conn);
        }
    }

    private long insertAndGetConnectorId(MConnector mc, Connection conn) {
        PreparedStatement baseConnectorStmt = null;
        try {
            baseConnectorStmt = conn.prepareStatement(crudQueries.getStmtInsertIntoConfigurable(),
                    Statement.RETURN_GENERATED_KEYS);
            baseConnectorStmt.setString(1, mc.getUniqueName());
            baseConnectorStmt.setString(2, mc.getClassName());
            baseConnectorStmt.setString(3, mc.getVersion());
            baseConnectorStmt.setString(4, mc.getType().name());

            int baseConnectorCount = baseConnectorStmt.executeUpdate();
            if (baseConnectorCount != 1) {
                throw new SqoopException(CommonRepositoryError.COMMON_0009, Integer.toString(baseConnectorCount));
            }

            ResultSet rsetConnectorId = baseConnectorStmt.getGeneratedKeys();

            if (!rsetConnectorId.next()) {
                throw new SqoopException(CommonRepositoryError.COMMON_0010);
            }
            // connector configurable also have directions
            insertConnectorDirections(rsetConnectorId.getLong(1), mc.getSupportedDirections(), conn);
            return rsetConnectorId.getLong(1);
        } catch (SQLException ex) {
            throw new SqoopException(CommonRepositoryError.COMMON_0011, mc.toString(), ex);
        } finally {
            closeStatements(baseConnectorStmt);
        }
    }

    /**
     * Helper method to insert the configs from the MConnector into the
     * repository. The job and connector configs within <code>mc</code> will get
     * updated with the id of the configs when this function returns.
     * @param mc The connector to use for updating configs
     * @param conn JDBC connection to use for inserting the configs
     */
    private void insertConfigsForConnector(MConnector mc, Connection conn) {
        long connectorId = mc.getPersistenceId();
        PreparedStatement baseConfigStmt = null;
        PreparedStatement baseInputStmt = null;
        try {
            baseConfigStmt = conn.prepareStatement(crudQueries.getStmtInsertIntoConfig(),
                    Statement.RETURN_GENERATED_KEYS);

            baseInputStmt = conn.prepareStatement(crudQueries.getStmtInsertIntoInput(),
                    Statement.RETURN_GENERATED_KEYS);

            // Register link type config
            registerConfigs(connectorId, null /* No direction for LINK type config */,
                    mc.getLinkConfig().getConfigs(), MConfigType.LINK.name(), baseConfigStmt, baseInputStmt, conn);

            // Register both from/to job type config for connector
            if (mc.getSupportedDirections().isDirectionSupported(Direction.FROM)) {
                registerConfigs(connectorId, Direction.FROM, mc.getFromConfig().getConfigs(),
                        MConfigType.JOB.name(), baseConfigStmt, baseInputStmt, conn);
            }
            if (mc.getSupportedDirections().isDirectionSupported(Direction.TO)) {
                registerConfigs(connectorId, Direction.TO, mc.getToConfig().getConfigs(), MConfigType.JOB.name(),
                        baseConfigStmt, baseInputStmt, conn);
            }
        } catch (SQLException ex) {
            throw new SqoopException(CommonRepositoryError.COMMON_0011, mc.toString(), ex);
        } finally {
            closeStatements(baseConfigStmt, baseInputStmt);
        }
    }

    private long insertAndGetDriverId(MDriver mDriver, Connection conn) {
        PreparedStatement baseDriverStmt = null;
        try {
            baseDriverStmt = conn.prepareStatement(crudQueries.getStmtInsertIntoConfigurable(),
                    Statement.RETURN_GENERATED_KEYS);
            baseDriverStmt.setString(1, mDriver.getUniqueName());
            baseDriverStmt.setString(2, Driver.getClassName());
            baseDriverStmt.setString(3, mDriver.getVersion());
            baseDriverStmt.setString(4, mDriver.getType().name());

            int baseDriverCount = baseDriverStmt.executeUpdate();
            if (baseDriverCount != 1) {
                throw new SqoopException(CommonRepositoryError.COMMON_0009, Integer.toString(baseDriverCount));
            }

            ResultSet rsetDriverId = baseDriverStmt.getGeneratedKeys();

            if (!rsetDriverId.next()) {
                throw new SqoopException(CommonRepositoryError.COMMON_0010);
            }
            return rsetDriverId.getLong(1);
        } catch (SQLException ex) {
            throw new SqoopException(CommonRepositoryError.COMMON_0044, mDriver.toString(), ex);
        } finally {
            closeStatements(baseDriverStmt);
        }
    }

    /**
     * Stores counters for given submission in repository.
     *
     * @param submissionId Submission id
     * @param counters Counters that should be stored
     * @param conn Connection to derby repository
     * @throws java.sql.SQLException
     */
    private void createSubmissionCounters(long submissionId, Counters counters, Connection conn)
            throws SQLException {
        PreparedStatement stmt = null;

        try {
            stmt = conn.prepareStatement(crudQueries.getStmtInsertCounterSubmission());

            for (CounterGroup group : counters) {
                long groupId = getCounterGroupId(group, conn);

                for (Counter counter : group) {
                    long counterId = getCounterId(counter, conn);

                    stmt.setLong(1, groupId);
                    stmt.setLong(2, counterId);
                    stmt.setLong(3, submissionId);
                    stmt.setLong(4, counter.getValue());

                    stmt.executeUpdate();
                }
            }
        } finally {
            closeStatements(stmt);
        }
    }

    /**
     * Resolves counter group database id.
     *
     * @param group Given group
     * @param conn Connection to database
     * @return Id
     * @throws java.sql.SQLException
     */
    private long getCounterGroupId(CounterGroup group, Connection conn) throws SQLException {
        PreparedStatement select = null;
        PreparedStatement insert = null;
        ResultSet rsSelect = null;
        ResultSet rsInsert = null;

        try {
            select = conn.prepareStatement(crudQueries.getStmtSelectCounterGroup());
            select.setString(1, group.getName());

            rsSelect = select.executeQuery();

            if (rsSelect.next()) {
                return rsSelect.getLong(1);
            }

            insert = conn.prepareStatement(crudQueries.getStmtInsertCounterGroup(),
                    Statement.RETURN_GENERATED_KEYS);
            insert.setString(1, group.getName());
            insert.executeUpdate();

            rsInsert = insert.getGeneratedKeys();

            if (!rsInsert.next()) {
                throw new SqoopException(CommonRepositoryError.COMMON_0010);
            }

            return rsInsert.getLong(1);
        } finally {
            closeResultSets(rsSelect, rsInsert);
            closeStatements(select, insert);
        }
    }

    /**
     * Resolves counter id.
     *
     * @param counter Given counter
     * @param conn Connection to database
     * @return Id
     * @throws java.sql.SQLException
     */
    private long getCounterId(Counter counter, Connection conn) throws SQLException {
        PreparedStatement select = null;
        PreparedStatement insert = null;
        ResultSet rsSelect = null;
        ResultSet rsInsert = null;

        try {
            select = conn.prepareStatement(crudQueries.getStmtSelectCounter());
            select.setString(1, counter.getName());

            rsSelect = select.executeQuery();

            if (rsSelect.next()) {
                return rsSelect.getLong(1);
            }

            insert = conn.prepareStatement(crudQueries.getStmtInsertCounter(), Statement.RETURN_GENERATED_KEYS);
            insert.setString(1, counter.getName());
            insert.executeUpdate();

            rsInsert = insert.getGeneratedKeys();

            if (!rsInsert.next()) {
                throw new SqoopException(CommonRepositoryError.COMMON_0010);
            }

            return rsInsert.getLong(1);
        } finally {
            closeResultSets(rsSelect, rsInsert);
            closeStatements(select, insert);
        }
    }

    /**
     * Create MSubmission structure from result set.
     *
     * @param rs Result set, only active row will be fetched
     * @param conn Connection to database
     * @return Created MSubmission structure
     * @throws java.sql.SQLException
     */
    private MSubmission loadSubmission(ResultSet rs, Connection conn) throws SQLException {
        MSubmission submission = new MSubmission();

        submission.setPersistenceId(rs.getLong(1));
        submission.setJobId(rs.getLong(2));
        submission.setStatus(SubmissionStatus.valueOf(rs.getString(3)));
        submission.setCreationUser(rs.getString(4));
        submission.setCreationDate(rs.getTimestamp(5));
        submission.setLastUpdateUser(rs.getString(6));
        submission.setLastUpdateDate(rs.getTimestamp(7));
        submission.setExternalJobId(rs.getString(8));
        submission.setExternalLink(rs.getString(9));
        SubmissionError error = new SubmissionError();
        error.setErrorSummary(rs.getString(10));
        error.setErrorDetails(rs.getString(11));
        submission.setError(error);
        Counters counters = loadCountersSubmission(rs.getLong(1), conn);
        submission.setCounters(counters);

        return submission;
    }

    private Counters loadCountersSubmission(long submissionId, Connection conn) throws SQLException {
        PreparedStatement stmt = null;
        ResultSet rs = null;
        try {
            stmt = conn.prepareStatement(crudQueries.getStmtSelectCounterSubmission());
            stmt.setLong(1, submissionId);
            rs = stmt.executeQuery();

            Counters counters = new Counters();

            while (rs.next()) {
                String groupName = rs.getString(1);
                String counterName = rs.getString(2);
                long value = rs.getLong(3);

                CounterGroup group = counters.getCounterGroup(groupName);
                if (group == null) {
                    group = new CounterGroup(groupName);
                    counters.addCounterGroup(group);
                }

                group.addCounter(new Counter(counterName, value));
            }

            if (counters.isEmpty()) {
                return null;
            } else {
                return counters;
            }
        } finally {
            closeStatements(stmt);
            closeResultSets(rs);
        }
    }

    private Long getDirection(Direction direction, Connection conn) throws SQLException {
        PreparedStatement directionStmt = null;
        ResultSet rs = null;

        try {
            directionStmt = conn.prepareStatement(crudQueries.getStmtSelectSqdIdBySqdName());
            directionStmt.setString(1, direction.toString());
            rs = directionStmt.executeQuery();

            rs.next();
            return rs.getLong(1);
        } finally {
            if (rs != null) {
                closeResultSets(rs);
            }
            if (directionStmt != null) {
                closeStatements(directionStmt);
            }
        }
    }

    private Direction getDirection(long directionId, Connection conn) throws SQLException {
        PreparedStatement directionStmt = null;
        ResultSet rs = null;

        try {
            directionStmt = conn.prepareStatement(crudQueries.getStmtSelectSqdNameBySqdId());
            directionStmt.setLong(1, directionId);
            rs = directionStmt.executeQuery();

            rs.next();
            return Direction.valueOf(rs.getString(1));
        } finally {
            if (rs != null) {
                closeResultSets(rs);
            }
            if (directionStmt != null) {
                closeStatements(directionStmt);
            }
        }
    }

    private SupportedDirections findConnectorSupportedDirections(long connectorId, Connection conn)
            throws SQLException {
        PreparedStatement connectorDirectionsStmt = null;
        ResultSet rs = null;

        boolean from = false, to = false;

        try {
            connectorDirectionsStmt = conn.prepareStatement(crudQueries.getStmtSelectSqConnectorDirections());
            connectorDirectionsStmt.setLong(1, connectorId);
            rs = connectorDirectionsStmt.executeQuery();

            while (rs.next()) {
                switch (getDirection(rs.getLong(2), conn)) {
                case FROM:
                    from = true;
                    break;

                case TO:
                    to = true;
                    break;
                }
            }
        } finally {
            if (rs != null) {
                closeResultSets(rs);
            }
            if (connectorDirectionsStmt != null) {
                closeStatements(connectorDirectionsStmt);
            }
        }

        return new SupportedDirections(from, to);
    }

    private List<MConnector> loadConnectors(PreparedStatement stmt, Connection conn) throws SQLException {
        List<MConnector> connectors = new ArrayList<MConnector>();
        ResultSet rsConnectors = null;
        PreparedStatement connectorConfigFetchStmt = null;
        PreparedStatement connectorConfigInputFetchStmt = null;

        try {
            rsConnectors = stmt.executeQuery();
            connectorConfigFetchStmt = conn.prepareStatement(crudQueries.getStmtSelectConfigForConfigurable());
            connectorConfigInputFetchStmt = conn.prepareStatement(crudQueries.getStmtSelectInput());

            while (rsConnectors.next()) {
                long connectorId = rsConnectors.getLong(1);
                String connectorName = rsConnectors.getString(2);
                String connectorClassName = rsConnectors.getString(3);
                String connectorVersion = rsConnectors.getString(4);

                connectorConfigFetchStmt.setLong(1, connectorId);

                List<MConfig> linkConfig = new ArrayList<MConfig>();
                List<MConfig> fromConfig = new ArrayList<MConfig>();
                List<MConfig> toConfig = new ArrayList<MConfig>();

                loadConnectorConfigs(linkConfig, fromConfig, toConfig, connectorConfigFetchStmt,
                        connectorConfigInputFetchStmt, 1, conn);

                SupportedDirections supportedDirections = findConnectorSupportedDirections(connectorId, conn);
                MFromConfig fromJobConfig = null;
                MToConfig toJobConfig = null;
                if (supportedDirections.isDirectionSupported(Direction.FROM)) {
                    fromJobConfig = new MFromConfig(fromConfig);
                }
                if (supportedDirections.isDirectionSupported(Direction.TO)) {
                    toJobConfig = new MToConfig(toConfig);
                }
                MConnector mc = new MConnector(connectorName, connectorClassName, connectorVersion,
                        new MLinkConfig(linkConfig), fromJobConfig, toJobConfig);
                mc.setPersistenceId(connectorId);

                connectors.add(mc);
            }
        } finally {
            closeResultSets(rsConnectors);
            closeStatements(connectorConfigFetchStmt, connectorConfigInputFetchStmt);
        }
        return connectors;
    }

    private List<MLink> loadLinks(PreparedStatement stmt, Connection conn) throws SQLException {
        List<MLink> links = new ArrayList<MLink>();
        ResultSet rsConnection = null;
        PreparedStatement connectorConfigFetchStatement = null;
        PreparedStatement connectorConfigInputStatement = null;

        try {
            rsConnection = stmt.executeQuery();

            connectorConfigFetchStatement = conn.prepareStatement(crudQueries.getStmtSelectConfigForConfigurable());
            connectorConfigInputStatement = conn.prepareStatement(crudQueries.getStmtFetchLinkInput());

            while (rsConnection.next()) {
                long id = rsConnection.getLong(1);
                String name = rsConnection.getString(2);
                long connectorId = rsConnection.getLong(3);
                boolean enabled = rsConnection.getBoolean(4);
                String creationUser = rsConnection.getString(5);
                Date creationDate = rsConnection.getTimestamp(6);
                String updateUser = rsConnection.getString(7);
                Date lastUpdateDate = rsConnection.getTimestamp(8);

                connectorConfigFetchStatement.setLong(1, connectorId);
                connectorConfigInputStatement.setLong(1, id);

                List<MConfig> connectorLinkConfig = new ArrayList<MConfig>();
                List<MConfig> fromConfig = new ArrayList<MConfig>();
                List<MConfig> toConfig = new ArrayList<MConfig>();

                loadConnectorConfigs(connectorLinkConfig, fromConfig, toConfig, connectorConfigFetchStatement,
                        connectorConfigInputStatement, 2, conn);
                MLink link = new MLink(connectorId, new MLinkConfig(connectorLinkConfig));

                link.setPersistenceId(id);
                link.setName(name);
                link.setCreationUser(creationUser);
                link.setCreationDate(creationDate);
                link.setLastUpdateUser(updateUser);
                link.setLastUpdateDate(lastUpdateDate);
                link.setEnabled(enabled);

                links.add(link);
            }
        } finally {
            closeResultSets(rsConnection);
            closeStatements(connectorConfigFetchStatement, connectorConfigInputStatement);
        }

        return links;
    }

    private List<MJob> loadJobs(PreparedStatement stmt, Connection conn) throws SQLException {
        List<MJob> jobs = new ArrayList<MJob>();
        ResultSet rsJob = null;
        PreparedStatement fromConfigFetchStmt = null;
        PreparedStatement toConfigFetchStmt = null;
        PreparedStatement driverConfigfetchStmt = null;
        PreparedStatement jobInputFetchStmt = null;

        try {
            rsJob = stmt.executeQuery();
            // Note: Job does not hold a explicit reference to the driver since every
            // job has the same driver
            long driverId = this.findDriver(MDriver.DRIVER_NAME, conn).getPersistenceId();
            fromConfigFetchStmt = conn.prepareStatement(crudQueries.getStmtSelectConfigForConfigurable());
            toConfigFetchStmt = conn.prepareStatement(crudQueries.getStmtSelectConfigForConfigurable());
            driverConfigfetchStmt = conn.prepareStatement(crudQueries.getStmtSelectConfigForConfigurable());
            jobInputFetchStmt = conn.prepareStatement(crudQueries.getStmtFetchJobInput());

            while (rsJob.next()) {
                long fromConnectorId = rsJob.getLong(1);
                long toConnectorId = rsJob.getLong(2);
                long id = rsJob.getLong(3);
                String name = rsJob.getString(4);
                long fromLinkId = rsJob.getLong(5);
                long toLinkId = rsJob.getLong(6);
                boolean enabled = rsJob.getBoolean(7);
                String createBy = rsJob.getString(8);
                Date creationDate = rsJob.getTimestamp(9);
                String updateBy = rsJob.getString(10);
                Date lastUpdateDate = rsJob.getTimestamp(11);

                fromConfigFetchStmt.setLong(1, fromConnectorId);
                toConfigFetchStmt.setLong(1, toConnectorId);
                driverConfigfetchStmt.setLong(1, driverId);

                jobInputFetchStmt.setLong(1, id);

                // FROM entity configs
                List<MConfig> fromConnectorLinkConfig = new ArrayList<MConfig>();
                List<MConfig> fromConnectorFromJobConfig = new ArrayList<MConfig>();
                List<MConfig> fromConnectorToJobConfig = new ArrayList<MConfig>();

                loadConnectorConfigs(fromConnectorLinkConfig, fromConnectorFromJobConfig, fromConnectorToJobConfig,
                        fromConfigFetchStmt, jobInputFetchStmt, 2, conn);

                // TO entity configs
                List<MConfig> toConnectorLinkConfig = new ArrayList<MConfig>();
                List<MConfig> toConnectorFromJobConfig = new ArrayList<MConfig>();
                List<MConfig> toConnectorToJobConfig = new ArrayList<MConfig>();

                List<MConfig> driverConfig = new ArrayList<MConfig>();

                loadConnectorConfigs(toConnectorLinkConfig, toConnectorFromJobConfig, toConnectorToJobConfig,
                        toConfigFetchStmt, jobInputFetchStmt, 2, conn);

                loadDriverConfigs(driverConfig, driverConfigfetchStmt, jobInputFetchStmt, 2, conn);

                MJob job = new MJob(fromConnectorId, toConnectorId, fromLinkId, toLinkId,
                        new MFromConfig(fromConnectorFromJobConfig), new MToConfig(toConnectorToJobConfig),
                        new MDriverConfig(driverConfig));

                job.setPersistenceId(id);
                job.setName(name);
                job.setCreationUser(createBy);
                job.setCreationDate(creationDate);
                job.setLastUpdateUser(updateBy);
                job.setLastUpdateDate(lastUpdateDate);
                job.setEnabled(enabled);

                jobs.add(job);
            }
        } finally {
            closeResultSets(rsJob);
            closeStatements(fromConfigFetchStmt, toConfigFetchStmt, driverConfigfetchStmt, jobInputFetchStmt);
        }

        return jobs;
    }

    private void registerConfigDirection(Long configId, Direction direction, Connection conn) throws SQLException {
        PreparedStatement stmt = null;
        try {
            stmt = conn.prepareStatement(crudQueries.getStmtInsertSqConfigDirections());
            stmt.setLong(1, configId);
            stmt.setLong(2, getDirection(direction, conn));
            if (stmt.executeUpdate() != 1) {
                throw new SqoopException(CommonRepositoryError.COMMON_0042);
            }
        } finally {
            closeStatements(stmt);
        }
    }

    /**
     * Register configs in derby database. This method will insert the ids
     * generated by the repository into the configs passed in itself.
     *
     * Use given prepared statements to create entire config structure in database.
     *
     * @param configurableId
     * @param configs
     * @param type
     * @param baseConfigStmt
     * @param baseInputStmt
     * @param conn
     * @return short number of configs registered.
     * @throws java.sql.SQLException
     */
    private short registerConfigs(Long configurableId, Direction direction, List<MConfig> configs, String type,
            PreparedStatement baseConfigStmt, PreparedStatement baseInputStmt, Connection conn)
            throws SQLException {
        short configIndex = 0;

        for (MConfig config : configs) {
            baseConfigStmt.setLong(1, configurableId);

            baseConfigStmt.setString(2, config.getName());
            baseConfigStmt.setString(3, type);
            baseConfigStmt.setShort(4, configIndex++);

            int baseConfigCount = baseConfigStmt.executeUpdate();
            if (baseConfigCount != 1) {
                throw new SqoopException(CommonRepositoryError.COMMON_0012, Integer.toString(baseConfigCount));
            }
            ResultSet rsetConfigId = baseConfigStmt.getGeneratedKeys();
            if (!rsetConfigId.next()) {
                throw new SqoopException(CommonRepositoryError.COMMON_0013);
            }

            long configId = rsetConfigId.getLong(1);
            config.setPersistenceId(configId);

            if (direction != null) {
                registerConfigDirection(configId, direction, conn);
            }

            // Insert all the inputs
            List<MInput<?>> inputs = config.getInputs();
            registerConfigInputs(config, inputs, baseInputStmt);
            // validate all the input relations
            Map<Long, List<String>> inputRelationships = new HashMap<Long, List<String>>();
            for (MInput<?> input : inputs) {
                List<String> inputOverrides = validateAndGetOverridesAttribute(input, config);
                if (inputOverrides != null && inputOverrides.size() > 0) {
                    inputRelationships.put(input.getPersistenceId(), inputOverrides);
                }
            }

            // Insert all input relations
            if (inputRelationships != null && inputRelationships.size() > 0) {
                for (Map.Entry<Long, List<String>> entry : inputRelationships.entrySet()) {
                    List<String> children = entry.getValue();
                    for (String child : children) {
                        Long childId = config.getInput(child).getPersistenceId();
                        insertConfigInputRelationship(entry.getKey(), childId, conn);
                    }
                }
            }
        }
        return configIndex;
    }

    /**
     * Save given inputs to the database.
     *
     * Use given prepare statement to save all inputs into repository.
     *
     * @param config
     *          corresponding config
     * @param inputs
     *          List of inputs that needs to be saved
     * @param baseInputStmt
     *          Statement that we can utilize
     * @throws java.sql.SQLException
     *           In case of any failure on Derby side
     */
    private void registerConfigInputs(MConfig config, List<MInput<?>> inputs, PreparedStatement baseInputStmt)
            throws SQLException {

        short inputIndex = 0;
        for (MInput<?> input : inputs) {
            baseInputStmt.setString(1, input.getName());
            baseInputStmt.setLong(2, config.getPersistenceId());
            baseInputStmt.setShort(3, inputIndex++);
            baseInputStmt.setString(4, input.getType().name());
            baseInputStmt.setBoolean(5, input.isSensitive());
            // String specific column(s)
            if (input.getType().equals(MInputType.STRING)) {
                MStringInput strInput = (MStringInput) input;
                baseInputStmt.setShort(6, strInput.getMaxLength());
            } else {
                baseInputStmt.setNull(6, Types.INTEGER);
            }

            baseInputStmt.setString(7, input.getEditable().name());

            // Enum specific column(s)
            if (input.getType() == MInputType.ENUM) {
                baseInputStmt.setString(8, StringUtils.join(((MEnumInput) input).getValues(), ","));
            } else {
                baseInputStmt.setNull(8, Types.VARCHAR);
            }

            int baseInputCount = baseInputStmt.executeUpdate();
            if (baseInputCount != 1) {
                throw new SqoopException(CommonRepositoryError.COMMON_0014, Integer.toString(baseInputCount));
            }

            ResultSet rsetInputId = baseInputStmt.getGeneratedKeys();
            if (!rsetInputId.next()) {
                throw new SqoopException(CommonRepositoryError.COMMON_0015);
            }

            long inputId = rsetInputId.getLong(1);
            input.setPersistenceId(inputId);
        }
    }

    private void insertConfigInputRelationship(Long parent, Long child, Connection conn) {
        PreparedStatement baseInputRelationStmt = null;
        try {
            baseInputRelationStmt = conn.prepareStatement(
                    CommonRepositoryInsertUpdateDeleteSelectQuery.STMT_INSERT_INTO_INPUT_RELATION);
            baseInputRelationStmt.setLong(1, parent);
            baseInputRelationStmt.setLong(2, child);
            baseInputRelationStmt.executeUpdate();

        } catch (SQLException ex) {
            throw new SqoopException(CommonRepositoryError.COMMON_0047, ex);
        } finally {
            closeStatements(baseInputRelationStmt);
        }
    }

    /**
     * Validate that the input override attribute adheres to the rules imposed
     * NOTE: all input names in a config class will and must be unique
     * Rule #1.
     * If editable == USER_ONLY ( cannot override itself ) can override other  CONNECTOR_ONLY and ANY inputs,
     * but cannot overriding other USER_ONLY attributes
     * Rule #2.
     * If editable == CONNECTOR_ONLY or ANY ( cannot override itself ) can override any other attribute in the config object
     * @param currentInput
     *
     */
    private List<String> validateAndGetOverridesAttribute(MInput<?> currentInput, MConfig config) {

        // split the overrides string into comma separated list
        String overrides = currentInput.getOverrides();
        if (StringUtils.isEmpty(overrides)) {
            return null;
        }
        String[] overrideInputs = overrides.split("\\,");
        List<String> children = new ArrayList<String>();

        for (String override : overrideInputs) {
            if (override.equals(currentInput.getName())) {
                throw new SqoopException(CommonRepositoryError.COMMON_0046,
                        "for input :" + currentInput.toString());
            }
            if (currentInput.getEditable().equals(InputEditable.USER_ONLY)) {
                if (config.getUserOnlyEditableInputNames().contains(override)) {
                    throw new SqoopException(CommonRepositoryError.COMMON_0045,
                            "for input :" + currentInput.toString());
                }
            }
            children.add(override);
        }
        return children;
    }

    @Override
    public MConfig findFromJobConfig(long jobId, String configName, Connection conn) {
        MFromConfig fromConfigs = findJob(jobId, conn).getFromJobConfig();
        if (fromConfigs != null) {
            MConfig config = fromConfigs.getConfig(configName);
            if (config == null) {
                throw new SqoopException(CommonRepositoryError.COMMON_0049, "for configName :" + configName);
            }
            return config;
        }
        throw new SqoopException(CommonRepositoryError.COMMON_0049, "for configName :" + configName);
    }

    @Override
    public MConfig findToJobConfig(long jobId, String configName, Connection conn) {
        MToConfig toConfigs = findJob(jobId, conn).getToJobConfig();
        if (toConfigs != null) {
            MConfig config = toConfigs.getConfig(configName);
            if (config == null) {
                throw new SqoopException(CommonRepositoryError.COMMON_0050, "for configName :" + configName);
            }
            return config;
        }
        throw new SqoopException(CommonRepositoryError.COMMON_0050, "for configName :" + configName);
    }

    @Override
    public MConfig findDriverJobConfig(long jobId, String configName, Connection conn) {
        MDriverConfig driverConfigs = findJob(jobId, conn).getDriverConfig();
        if (driverConfigs != null) {
            MConfig config = driverConfigs.getConfig(configName);
            if (config == null) {
                throw new SqoopException(CommonRepositoryError.COMMON_0051, "for configName :" + configName);
            }
            return config;
        }
        throw new SqoopException(CommonRepositoryError.COMMON_0051, "for configName :" + configName);
    }

    @Override
    public MConfig findLinkConfig(long linkId, String configName, Connection conn) {
        MConfig driverConfig = findLink(linkId, conn).getConnectorLinkConfig(configName);
        if (driverConfig == null) {
            throw new SqoopException(CommonRepositoryError.COMMON_0052, "for configName :" + configName);
        }
        return driverConfig;
    }

    @SuppressWarnings("resource")
    @Override
    public void updateJobConfig(long jobId, MConfig config, MConfigUpdateEntityType type, Connection conn) {
        List<MInput<?>> inputs = config.getInputs();
        PreparedStatement updateStmt = null;

        try {
            updateStmt = conn.prepareStatement(crudQueries.getStmtUpdateJobInput());
            for (MInput<?> input : inputs) {
                if (input.isEmpty()) {
                    continue;
                }
                validateEditableConstraints(type, input);
                updateStmt.setString(1, input.getUrlSafeValueString());
                updateStmt.setLong(2, input.getPersistenceId());
                updateStmt.setLong(3, jobId);
                updateStmt.executeUpdate();
            }
        } catch (SQLException ex) {
            logException(ex, jobId);
            throw new SqoopException(CommonRepositoryError.COMMON_0053, ex);
        } finally {
            closeStatements(updateStmt);
        }
    }

    private void validateEditableConstraints(MConfigUpdateEntityType type, MInput<?> input) {
        if (input.getEditable().equals(InputEditable.CONNECTOR_ONLY) && type.equals(MConfigUpdateEntityType.USER)) {
            throw new SqoopException(CommonRepositoryError.COMMON_0055);
        }
        if (input.getEditable().equals(InputEditable.USER_ONLY) && type.equals(MConfigUpdateEntityType.CONNECTOR)) {
            throw new SqoopException(CommonRepositoryError.COMMON_0056);
        }
    }

    @Override
    public void updateLinkConfig(long linkId, MConfig config, MConfigUpdateEntityType type, Connection conn) {
        List<MInput<?>> inputs = config.getInputs();
        PreparedStatement updateStmt = null;
        try {
            updateStmt = conn.prepareStatement(crudQueries.getStmtUpdateLinkInput());
            for (MInput<?> input : inputs) {
                if (input.isEmpty()) {
                    continue;
                }
                validateEditableConstraints(type, input);
                updateStmt.setString(1, input.getUrlSafeValueString());
                updateStmt.setLong(2, input.getPersistenceId());
                updateStmt.setLong(3, linkId);
                updateStmt.executeUpdate();
            }
        } catch (SQLException ex) {
            logException(ex, linkId);
            throw new SqoopException(CommonRepositoryError.COMMON_0054, ex);
        } finally {
            closeStatements(updateStmt);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String validationQuery() {
        return "values(1)"; // Yes, this is valid PostgreSQL SQL
    }

    /**
     * Load configs and corresponding inputs from Derby database.
     *
     * Use given prepared statements to load all configs and corresponding inputs
     * from Derby.
     *
     * @param driverConfig List of driver configs that will be filled up
     * @param configFetchStatement Prepared statement for fetching configs
     * @param inputFetchStmt Prepare statement for fetching inputs
     * @param configPosition position of the config
     * @throws java.sql.SQLException In case of any failure on Derby side
     */
    private void loadDriverConfigs(List<MConfig> driverConfig, PreparedStatement configFetchStatement,
            PreparedStatement inputFetchStmt, int configPosition, Connection conn) throws SQLException {

        // Get list of structures from database
        ResultSet rsetConfig = configFetchStatement.executeQuery();
        while (rsetConfig.next()) {
            long configId = rsetConfig.getLong(1);
            Long fromConnectorId = rsetConfig.getLong(2);
            String configName = rsetConfig.getString(3);
            String configTYpe = rsetConfig.getString(4);
            int configIndex = rsetConfig.getInt(5);
            List<MInput<?>> configInputs = new ArrayList<MInput<?>>();

            MConfig mDriverConfig = new MConfig(configName, configInputs);
            mDriverConfig.setPersistenceId(configId);

            inputFetchStmt.setLong(configPosition, configId);

            ResultSet rsetInput = inputFetchStmt.executeQuery();
            while (rsetInput.next()) {
                long inputId = rsetInput.getLong(1);
                String inputName = rsetInput.getString(2);
                long inputConfig = rsetInput.getLong(3);
                short inputIndex = rsetInput.getShort(4);
                String inputType = rsetInput.getString(5);
                boolean inputSensitivity = rsetInput.getBoolean(6);
                short inputStrLength = rsetInput.getShort(7);
                String editable = rsetInput.getString(8);
                InputEditable editableEnum = editable != null ? InputEditable.valueOf(editable) : InputEditable.ANY;
                // get the overrides value from the SQ_INPUT_RELATION table
                String overrides = getOverrides(inputId, conn);
                String inputEnumValues = rsetInput.getString(9);
                String value = rsetInput.getString(10);

                MInputType mit = MInputType.valueOf(inputType);
                MInput input = null;
                switch (mit) {
                case STRING:
                    input = new MStringInput(inputName, inputSensitivity, editableEnum, overrides, inputStrLength);
                    break;
                case MAP:
                    input = new MMapInput(inputName, inputSensitivity, editableEnum, overrides);
                    break;
                case BOOLEAN:
                    input = new MBooleanInput(inputName, inputSensitivity, editableEnum, overrides);
                    break;
                case INTEGER:
                    input = new MIntegerInput(inputName, inputSensitivity, editableEnum, overrides);
                    break;
                case LONG:
                    input = new MLongInput(inputName, inputSensitivity, editableEnum, overrides);
                    break;
                case ENUM:
                    input = new MEnumInput(inputName, inputSensitivity, editableEnum, overrides,
                            inputEnumValues.split(","));
                    break;
                default:
                    throw new SqoopException(CommonRepositoryError.COMMON_0003, "input-" + inputName + ":" + inputId
                            + ":" + "config-" + inputConfig + ":" + mit.name());
                }

                // Set persistent ID
                input.setPersistenceId(inputId);

                // Set value
                if (value == null) {
                    input.setEmpty();
                } else {
                    input.restoreFromUrlSafeValueString(value);
                }

                if (mDriverConfig.getInputs().size() != inputIndex) {
                    throw new SqoopException(CommonRepositoryError.COMMON_0006,
                            "config: " + mDriverConfig + "; input: " + input + "; index: " + inputIndex
                                    + "; expected: " + mDriverConfig.getInputs().size());
                }

                mDriverConfig.getInputs().add(input);
            }

            if (mDriverConfig.getInputs().size() == 0) {
                throw new SqoopException(CommonRepositoryError.COMMON_0005,
                        "owner-" + fromConnectorId + "; config: " + mDriverConfig);
            }

            MConfigType configType = MConfigType.valueOf(configTYpe);
            switch (configType) {
            case JOB:
                if (driverConfig.size() != configIndex) {
                    throw new SqoopException(CommonRepositoryError.COMMON_0007,
                            "owner-" + fromConnectorId + "; config: " + configType + "; index: " + configIndex
                                    + "; expected: " + driverConfig.size());
                }
                driverConfig.add(mDriverConfig);
                break;
            case CONNECTION:
                // do nothing
                break;
            default:
                throw new SqoopException(CommonRepositoryError.COMMON_0004,
                        "connector-" + fromConnectorId + ":" + configType);
            }
        }
    }

    private Direction findConfigDirection(long configId, Connection conn) throws SQLException {
        PreparedStatement stmt = null;
        ResultSet rs = null;

        try {
            stmt = conn.prepareStatement(crudQueries.getStmtSelectSqConfigDirections());
            stmt.setLong(1, configId);
            rs = stmt.executeQuery();
            rs.next();
            return getDirection(rs.getLong(2), conn);
        } finally {
            if (rs != null) {
                closeResultSets(rs);
            }
            if (stmt != null) {
                closeStatements(stmt);
            }
        }
    }

    /**
     * Load configs and corresponding inputs related to a connector.
     *
     * Use given prepared statements to load all configs and corresponding inputs
     * from Derby.
     *
     * @param linkConfig List of link configs that will be filled up
     * @param fromConfig FROM job configs that will be filled up
     * @param toConfig TO job configs that will be filled up
     * @param configFetchStmt Prepared statement for fetching configs
     * @param inputFetchStmt Prepare statement for fetching inputs
     * @param conn Connection object that is used to find config direction.
     * @throws java.sql.SQLException In case of any failure on Derby side
     */
    public void loadConnectorConfigs(List<MConfig> linkConfig, List<MConfig> fromConfig, List<MConfig> toConfig,
            PreparedStatement configFetchStmt, PreparedStatement inputFetchStmt, int configPosition,
            Connection conn) throws SQLException {

        // Get list of structures from database
        ResultSet rsetConfig = configFetchStmt.executeQuery();
        while (rsetConfig.next()) {
            long configId = rsetConfig.getLong(1);
            Long configConnectorId = rsetConfig.getLong(2);
            String configName = rsetConfig.getString(3);
            String configType = rsetConfig.getString(4);
            int configIndex = rsetConfig.getInt(5);
            List<MInput<?>> configInputs = new ArrayList<MInput<?>>();

            MConfig config = new MConfig(configName, configInputs);
            config.setPersistenceId(configId);

            inputFetchStmt.setLong(configPosition, configId);

            ResultSet rsetInput = inputFetchStmt.executeQuery();
            while (rsetInput.next()) {
                long inputId = rsetInput.getLong(1);
                String inputName = rsetInput.getString(2);
                long inputConfig = rsetInput.getLong(3);
                short inputIndex = rsetInput.getShort(4);
                String inputType = rsetInput.getString(5);
                boolean inputSensitivity = rsetInput.getBoolean(6);
                short inputStrLength = rsetInput.getShort(7);
                String editable = rsetInput.getString(8);
                InputEditable editableEnum = editable != null ? InputEditable.valueOf(editable) : InputEditable.ANY;
                // get the overrides value from the SQ_INPUT_RELATION table
                String overrides = getOverrides(inputId, conn);
                String inputEnumValues = rsetInput.getString(9);
                String value = rsetInput.getString(10);

                MInputType mit = MInputType.valueOf(inputType);

                MInput<?> input = null;
                switch (mit) {
                case STRING:
                    input = new MStringInput(inputName, inputSensitivity, editableEnum, overrides, inputStrLength);
                    break;
                case MAP:
                    input = new MMapInput(inputName, inputSensitivity, editableEnum, overrides);
                    break;
                case BOOLEAN:
                    input = new MBooleanInput(inputName, inputSensitivity, editableEnum, overrides);
                    break;
                case INTEGER:
                    input = new MIntegerInput(inputName, inputSensitivity, editableEnum, overrides);
                    break;
                case LONG:
                    input = new MLongInput(inputName, inputSensitivity, editableEnum, overrides);
                    break;
                case ENUM:
                    input = new MEnumInput(inputName, inputSensitivity, editableEnum, overrides,
                            inputEnumValues.split(","));
                    break;
                default:
                    throw new SqoopException(CommonRepositoryError.COMMON_0003, "input-" + inputName + ":" + inputId
                            + ":" + "config-" + inputConfig + ":" + mit.name());
                }

                // Set persistent ID
                input.setPersistenceId(inputId);

                // Set value
                if (value == null) {
                    input.setEmpty();
                } else {
                    input.restoreFromUrlSafeValueString(value);
                }

                if (config.getInputs().size() != inputIndex) {
                    throw new SqoopException(CommonRepositoryError.COMMON_0006, "config: " + config + "; input: "
                            + input + "; index: " + inputIndex + "; expected: " + config.getInputs().size());
                }

                config.getInputs().add(input);
            }

            if (config.getInputs().size() == 0) {
                throw new SqoopException(CommonRepositoryError.COMMON_0005,
                        "connector-" + configConnectorId + "; config: " + config);
            }

            MConfigType mConfigType = MConfigType.valueOf(configType);
            switch (mConfigType) {
            case CONNECTION:
            case LINK:
                if (linkConfig.size() != configIndex) {
                    throw new SqoopException(CommonRepositoryError.COMMON_0007,
                            "connector-" + configConnectorId + "; config: " + config + "; index: " + configIndex
                                    + "; expected: " + linkConfig.size());
                }
                linkConfig.add(config);
                break;
            case JOB:
                Direction type = findConfigDirection(configId, conn);
                List<MConfig> jobConfigs;
                switch (type) {
                case FROM:
                    jobConfigs = fromConfig;
                    break;

                case TO:
                    jobConfigs = toConfig;
                    break;

                default:
                    throw new SqoopException(DirectionError.DIRECTION_0000, "Direction: " + type);
                }

                if (jobConfigs.size() != configIndex) {
                    throw new SqoopException(CommonRepositoryError.COMMON_0007,
                            "connector-" + configConnectorId + "; config: " + config + "; index: " + configIndex
                                    + "; expected: " + jobConfigs.size());
                }

                jobConfigs.add(config);
                break;
            default:
                throw new SqoopException(CommonRepositoryError.COMMON_0004,
                        "connector-" + configConnectorId + ":" + config);
            }
        }
    }

    /**
     * @param inputId
     * @param conn
     * @return
     */
    private String getOverrides(long inputId, Connection conn) {

        PreparedStatement overridesStmt = null;
        PreparedStatement inputStmt = null;

        ResultSet rsOverride = null;
        ResultSet rsInput = null;

        List<String> overrides = new ArrayList<String>();
        try {
            overridesStmt = conn.prepareStatement(crudQueries.getStmtSelectInputOverrides());
            inputStmt = conn.prepareStatement(crudQueries.getStmtSelectInputById());
            overridesStmt.setLong(1, inputId);
            rsOverride = overridesStmt.executeQuery();

            while (rsOverride.next()) {
                long overrideId = rsOverride.getLong(1);
                inputStmt.setLong(1, overrideId);
                rsInput = inputStmt.executeQuery();
                if (rsInput.next()) {
                    overrides.add(rsInput.getString(2));
                }
            }
            if (overrides != null && overrides.size() > 0) {
                return StringUtils.join(overrides, ",");
            } else {
                return StringUtils.EMPTY;

            }
        } catch (SQLException ex) {
            logException(ex, inputId);
            throw new SqoopException(CommonRepositoryError.COMMON_0048, ex);
        } finally {
            if (rsOverride != null) {
                closeResultSets(rsOverride);
            }
            if (rsInput != null) {
                closeResultSets(rsInput);
            }

            if (overridesStmt != null) {
                closeStatements(overridesStmt);
            }
            if (inputStmt != null) {
                closeStatements(inputStmt);
            }
        }
    }

    private void createInputValues(String query, long id, List<MConfig> configs, Connection conn)
            throws SQLException {
        PreparedStatement stmt = null;
        int result;

        try {
            stmt = conn.prepareStatement(query);

            for (MConfig config : configs) {
                for (MInput<?> input : config.getInputs()) {
                    // Skip empty values as we're not interested in storing those in db
                    if (input.isEmpty()) {
                        continue;
                    }
                    stmt.setLong(1, id);
                    stmt.setLong(2, input.getPersistenceId());
                    stmt.setString(3, input.getUrlSafeValueString());

                    result = stmt.executeUpdate();
                    if (result != 1) {
                        throw new SqoopException(CommonRepositoryError.COMMON_0017, Integer.toString(result));
                    }
                }
            }
        } finally {
            closeStatements(stmt);
        }
    }

    /**
     * Execute given query via a PreparedStatement.
     * A list of args can be passed to the query.
     *
     * Example: runQuery("SELECT * FROM example WHERE test = ?", "test");
     *
     * @param query Query that should be executed
     * @param args Long, String, or Object arguments
     */
    protected void runQuery(String query, Connection conn, Object... args) {
        PreparedStatement stmt = null;
        try {
            stmt = conn.prepareStatement(query);

            for (int i = 0; i < args.length; ++i) {
                if (args[i] instanceof String) {
                    stmt.setString(i + 1, (String) args[i]);
                } else if (args[i] instanceof Long) {
                    stmt.setLong(i + 1, (Long) args[i]);
                } else {
                    stmt.setObject(i + 1, args[i]);
                }
            }

            if (stmt.execute()) {
                ResultSet rset = stmt.getResultSet();
                int count = 0;
                while (rset.next()) {
                    count++;
                }
                LOG.info("QUERY(" + query + ") produced unused resultset with " + count + " rows");
            } else {
                int updateCount = stmt.getUpdateCount();
                LOG.info("QUERY(" + query + ") Update count: " + updateCount);
            }
        } catch (SQLException ex) {
            throw new SqoopException(CommonRepositoryError.COMMON_0000, query, ex);
        } finally {
            closeStatements(stmt);
        }
    }

    /**
     * Close all given Results set.
     *
     * Any occurring exception is silently ignored and logged.
     *
     * @param resultSets Result sets to close
     */
    protected void closeResultSets(ResultSet... resultSets) {
        if (resultSets == null) {
            return;
        }
        for (ResultSet rs : resultSets) {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException ex) {
                    LOG.error("Exception during closing result set", ex);
                }
            }
        }
    }

    /**
     * Close all given statements.
     *
     * Any occurring exception is silently ignored and logged.
     *
     * @param stmts Statements to close
     */
    public void closeStatements(Statement... stmts) {
        if (stmts == null) {
            return;
        }
        for (Statement stmt : stmts) {
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException ex) {
                    LOG.error("Exception during closing statement", ex);
                }
            }
        }
    }

    /**
     * Log exception and all String variant of arbitrary number of objects.
     *
     * This method is useful to log SQLException with all objects that were
     * used in the query generation to see where is the issue.
     *
     * @param throwable Arbitrary throwable object
     * @param objects Arbitrary array of associated objects
     */
    protected void logException(Throwable throwable, Object... objects) {
        LOG.error("Exception in repository operation", throwable);
        LOG.error("Using database " + name());
        LOG.error("Associated objects: " + objects.length);
        for (Object object : objects) {
            LOG.error("\t" + object.getClass().getSimpleName() + ": " + object.toString());
        }
    }
}