com.dattack.dbtools.drules.engine.SourceExecutor.java Source code

Java tutorial

Introduction

Here is the source code for com.dattack.dbtools.drules.engine.SourceExecutor.java

Source

/*
 * Copyright (c) 2015, The Dattack team (http://www.dattack.com)
 *
 * Licensed 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 com.dattack.dbtools.drules.engine;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Callable;

import org.apache.commons.configuration.AbstractConfiguration;
import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.dattack.dbtools.drules.beans.ForEachBean;
import com.dattack.dbtools.drules.beans.Identifier;
import com.dattack.dbtools.drules.beans.Identifier.IdentifierBuilder;
import com.dattack.dbtools.drules.beans.SourceBean;
import com.dattack.dbtools.drules.beans.SourceCommandBean;
import com.dattack.dbtools.drules.beans.SourceCommandBeanVisitor;
import com.dattack.dbtools.drules.beans.SqlQueryBean;
import com.dattack.dbtools.drules.exceptions.DrulesNestableException;
import com.dattack.jtoolbox.commons.configuration.ConfigurationUtil;
import com.dattack.jtoolbox.jdbc.JNDIDataSource;

/**
 * @author cvarela
 * @since 0.1
 */
final class SourceExecutor implements Callable<SourceResult> {

    private static final Logger LOGGER = LoggerFactory.getLogger(SourceExecutor.class);

    private final SourceBean sourceBean;
    private final Configuration initialConfiguration;

    private class DefaultSourceCommandBeanVisitor implements SourceCommandBeanVisitor {

        private final AbstractConfiguration configuration;
        private final Connection connection;
        private final Map<Identifier, ResultSet> resultSetMap;
        private ResultSet lastResultSet;

        DefaultSourceCommandBeanVisitor(final Connection connection) {
            configuration = new CompositeConfiguration(ThreadContext.getInstance().getConfiguration());
            this.connection = connection;
            this.resultSetMap = new HashMap<>();
        }

        private void executeForEachLoop(final ForEachBean bean) {
            for (final SourceCommandBean child : bean.getCommandList()) {
                child.accept(this);
            }
        }

        private void forEachRef(final ForEachBean bean) {

            if (StringUtils.isBlank(bean.getRef())) {
                throw new NullPointerException("Invalid foreach loop (missing 'ref' value)");
            }

            final Identifier identifier = new IdentifierBuilder().withValue(bean.getRef()).build();
            try {

                final ResultSet resultSet = resultSetMap.get(identifier);
                if (resultSet == null) {
                    throw new NullPointerException(String.format("Missing ResultSet named '%s'", bean.getRef()));
                }

                do {
                    populateConfigurationFromResultSet(identifier, resultSet);
                    executeForEachLoop(bean);
                } while (resultSet.next());
            } catch (final SQLException e) {
                LOGGER.error(e.getMessage(), e);
            }
        }

        private void forEachValue(final ForEachBean bean) {

            for (final String value : bean.getValuesList()) {
                configuration.setProperty(bean.getKey(), value);
                executeForEachLoop(bean);
            }
        }

        private ResultSet getLastResultSet() {
            return lastResultSet;
        }

        private void populateConfigurationFromFirstRows() throws SQLException {

            for (final Entry<Identifier, ResultSet> entry : resultSetMap.entrySet()) {
                populateConfigurationFromFirstRows(entry.getKey(), entry.getValue());
            }
        }

        private void populateConfigurationFromFirstRows(final Identifier identifier, final ResultSet resultSet)
                throws SQLException {

            if (resultSet.isBeforeFirst() && resultSet.next() && identifier != null) {
                for (int columnIndex = 1; columnIndex <= resultSet.getMetaData().getColumnCount(); columnIndex++) {
                    final String columnName = resultSet.getMetaData().getColumnLabel(columnIndex);
                    final Object value = resultSet.getObject(columnIndex);
                    final String key = identifier.append(columnName).getValue();
                    configuration.setProperty(key, value);
                }
            }
        }

        private void populateConfigurationFromResultSet(final Identifier identifier, final ResultSet resultSet)
                throws SQLException {

            if (resultSet.isBeforeFirst()) {
                resultSet.next();
            }

            if (identifier != null) {
                for (int columnIndex = 1; columnIndex <= resultSet.getMetaData().getColumnCount(); columnIndex++) {
                    final String columnName = resultSet.getMetaData().getColumnLabel(columnIndex);
                    final Object value = resultSet.getObject(columnIndex);
                    final String key = identifier.append(columnName).getValue();
                    configuration.setProperty(key, value);
                }
            }
        }

        private void setLastValues(final SqlQueryBean sqlQueryBean, final ResultSet resultSet) {
            this.lastResultSet = resultSet;
            if (resultSet != null) {
                resultSetMap.put(sqlQueryBean.getId(), resultSet);
            }
        }

        @Override
        public void visit(final ForEachBean bean) {

            if (StringUtils.isNotBlank(bean.getRef())) {
                // check REF construction
                forEachRef(bean);
            } else {
                forEachValue(bean);
            }
        }

        @Override
        public void visit(final SqlQueryBean bean) {

            Statement statement = null;
            try {

                populateConfigurationFromFirstRows();

                final String interpolatedSql = ConfigurationUtil.interpolate(bean.getSql(), configuration);
                statement = connection.createStatement();
                final ResultSet resultSet = executeStatement(statement, interpolatedSql);
                setLastValues(bean, resultSet);

            } catch (final SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }

    private static Connection getConnection(final String jndiName) throws SQLException {
        return new JNDIDataSource(jndiName).getConnection();
    }

    SourceExecutor(final SourceBean sourceBean, final Configuration initialConfiguration) {
        this.sourceBean = sourceBean;
        this.initialConfiguration = initialConfiguration;
    }

    @Override
    public SourceResult call() throws DrulesNestableException {

        try {
            ThreadContext.getInstance().setInitialConfiguration(initialConfiguration);

            final String jndiName = getInterpolatedJndiName();

            LOGGER.info("Configuring datasource with JNDI name: '{}'", jndiName);
            final Connection connection = getConnection(jndiName);

            final DefaultSourceCommandBeanVisitor visitor = new DefaultSourceCommandBeanVisitor(connection);
            for (final Iterator<SourceCommandBean> it = sourceBean.getCommandList().iterator(); it.hasNext();) {
                final SourceCommandBean command = it.next();
                command.accept(visitor);
            }
            return new SourceResult(sourceBean.getId(), connection, visitor.getLastResultSet());
        } catch (final SQLException e) {
            throw new DrulesNestableException(e);
        }
    }

    private ResultSet executeStatement(final Statement statement, final String sql) throws SQLException {

        LOGGER.info("Executing SQL sentence [{}@{}]: {}", Thread.currentThread().getName(), sourceBean.getId(),
                sql);

        final boolean isResultSet = statement.execute(sql);

        if (isResultSet) {
            return statement.getResultSet();
        }

        return null;
    }

    private String getInterpolatedJndiName() {
        return ThreadContext.getInstance().interpolate(sourceBean.getJndi());
    }
}