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

Java tutorial

Introduction

Here is the source code for com.dattack.dbtools.drules.engine.DrulesEngine.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.lang.Thread.UncaughtExceptionHandler;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;

import javax.script.ScriptException;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.ConfigurationUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.dattack.dbtools.GlobalConfiguration;
import com.dattack.dbtools.drules.beans.ConfigurationBean;
import com.dattack.dbtools.drules.beans.DrulesBean;
import com.dattack.dbtools.drules.beans.DrulesParser;
import com.dattack.dbtools.drules.beans.EventActionEvalJsBean;
import com.dattack.dbtools.drules.beans.Identifier;
import com.dattack.dbtools.drules.beans.JoinBean;
import com.dattack.dbtools.drules.beans.NotificationActionBean;
import com.dattack.dbtools.drules.beans.NotificationActionBeanVisitor;
import com.dattack.dbtools.drules.beans.NotificationBean;
import com.dattack.dbtools.drules.beans.NotificationEventBean;
import com.dattack.dbtools.drules.beans.RowCheckBean;
import com.dattack.dbtools.drules.beans.SourceBean;
import com.dattack.dbtools.drules.beans.TaskBean;
import com.dattack.dbtools.drules.exceptions.DrulesNestableException;
import com.dattack.dbtools.drules.exceptions.IdentifierNotFoundException;
import com.dattack.jtoolbox.concurrent.SimpleThreadFactory.ThreadFactoryBuilder;
import com.dattack.jtoolbox.script.JavaScriptEngine;
import com.dattack.jtoolbox.util.CollectionUtils;

/**
 * @author cvarela
 * @since 0.1
 */
public class DrulesEngine {

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

    private final String drulesFilename;

    private DrulesBean drulesBean;

    private static ThreadFactory createThreadFactory() {
        return new ThreadFactoryBuilder() //
                .withThreadNamePrefix("source") //
                .withUncaughtExceptionHandler(new UncaughtExceptionHandler() {

                    @Override
                    public void uncaughtException(final Thread thread, final Throwable throwable) {
                        LOGGER.error("Uncaught exception throwed by thread '{}': {}", thread.getName(),
                                throwable.getMessage());
                    }
                }).build();
    }

    private static void execute(final TaskBean taskBean, final ConfigurationBean configurationBean)
            throws ConfigurationException, DrulesNestableException {

        LOGGER.info("Integrity task (Task ID: {}, Task name: {}): STARTED", taskBean.getId(), taskBean.getName());

        // start the flight recorder
        final FlightRecorder flightRecorder = new FlightRecorder(taskBean, configurationBean);

        executeJsEvals(taskBean);

        // executes the source' statements and retrieves the ResultSets to check
        final SourceResultGroup sourceResultGroup = getSourceResultsList(taskBean.getSources());

        try {
            // execute checks
            executeRowChecks(taskBean, sourceResultGroup, flightRecorder);
        } finally {

            // execute global checks
            // TODO: execute global checks

            // process the flight recorder and execute notifications
            if (taskBean.getNotification() != null) {
                executeNotifications(taskBean.getNotification(), flightRecorder);
            } else {
                final String notificationsFile = GlobalConfiguration
                        .getProperty(GlobalConfiguration.DRULES_NOTIFICATIONS_FILE_KEY);
                if (StringUtils.isNotBlank(notificationsFile)) {
                    final NotificationBean notification = DrulesParser.parseNotificationBean(notificationsFile);
                    executeNotifications(notification, flightRecorder);
                }
            }

            sourceResultGroup.close();

            LOGGER.info("Integrity task (Task ID: {}, Task name: {}): COMPLETED", taskBean.getId(),
                    taskBean.getName());
        }
    }

    private static void executeJsEvals(final TaskBean taskBean) throws DrulesNestableException {

        if (CollectionUtils.isNotEmpty(taskBean.getEvalList())) {
            for (final EventActionEvalJsBean item : taskBean.getEvalList()) {
                try {
                    final Object value = JavaScriptEngine.eval(item.getExpression());
                    ThreadContext.getInstance().setProperty(item.getName(), value);
                } catch (final ScriptException e) {
                    throw new DrulesNestableException(e);
                }
            }
        }
    }

    private static void executeNotification(final NotificationEventBean bean, final FlightRecorder flightRecorder) {

        final NotificationActionBeanVisitor visitor = new DefaultNotificationActionBeanVisitor(flightRecorder);
        for (final NotificationActionBean action : bean.getActionList()) {
            action.accept(visitor);
        }
    }

    private static void executeNotifications(final NotificationBean notificationBean,
            final FlightRecorder flightRecorder) {

        if (flightRecorder.hasErrors() && notificationBean.getOnError() != null) {
            executeNotification(notificationBean.getOnError(), flightRecorder);

        } else if (flightRecorder.hasWarnings() && notificationBean.getOnWarning() != null) {
            executeNotification(notificationBean.getOnWarning(), flightRecorder);

        } else if (notificationBean.getOnSuccess() != null) {
            executeNotification(notificationBean.getOnSuccess(), flightRecorder);
        }
    }

    private static void executeRowChecks(final TaskBean taskBean, final SourceResultGroup sourceResultList,
            final FlightRecorder flightRecorder) throws IllegalArgumentException, DrulesNestableException {

        for (final RowCheckBean rowCheck : taskBean.getRowChecks()) {
            // TODO: clone the sourceResultList to execute more than one loop
            if (taskBean.getRowChecks().size() > 1) {
                throw new IllegalArgumentException(
                        "TODO: clone the sourceResultList to execute more than one loop");
            }

            for (final JoinBean joinBean : rowCheck.getJoinList()) {
                JoinStrategyFactory.getInstance().create(joinBean, sourceResultList).execute(flightRecorder);
            }
        }
    }

    private static ConfigurationBean getConfigurationBean() throws ConfigurationException, DrulesNestableException {

        return DrulesParser.parseConfigurationBean(
                GlobalConfiguration.getProperty(GlobalConfiguration.DRULES_CONFIGURATION_FILE_KEY));
    }

    private static SourceResultGroup getSourceResultsList(final List<SourceBean> sourceList)
            throws DrulesNestableException {

        final ExecutorService executorService = Executors.newCachedThreadPool(createThreadFactory());

        final List<Future<SourceResult>> futureList = new ArrayList<>();

        for (final SourceBean sourceBean : sourceList) {
            futureList.add(executorService.submit(new SourceExecutor(sourceBean,
                    ConfigurationUtils.cloneConfiguration(ThreadContext.getInstance().getConfiguration()))));
        }

        final SourceResultGroup sourceResultList = new SourceResultGroup();

        for (final Future<SourceResult> future : futureList) {
            try {
                sourceResultList.add(future.get());
            } catch (InterruptedException | ExecutionException e) {
                throw new DrulesNestableException(e);
            }
        }
        executorService.shutdown();

        return sourceResultList;
    }

    public DrulesEngine(final String drulesFilename, final Configuration initialConfiguration) {
        this.drulesFilename = drulesFilename;
        ThreadContext.getInstance().setInitialConfiguration(initialConfiguration);
        ThreadContext.getInstance().setProperty(PropertyNames.EXECUTION_ID, System.currentTimeMillis());
    }

    /**
     * Executes the task defined in the file whose identifier matches the indicated.
     *
     * @param taskId
     *            the task identifier
     * @param initialConfiguration
     *            properties required to perform the task
     */
    public void execute(final Identifier taskId) throws DrulesNestableException {

        try {
            final TaskBean taskBean = getDrulesBean().getTask(taskId);
            if (taskBean == null) {
                throw new IdentifierNotFoundException(TaskBean.class, taskId);
            }

            final ConfigurationBean configurationBean = getConfigurationBean();

            LOGGER.info("SMTP hostname: " + configurationBean.getConfigurationSmtpBean().getHostname().toString());

            execute(taskBean, configurationBean);

        } catch (final ConfigurationException e) {
            throw new DrulesNestableException(e);
        }
    }

    private synchronized DrulesBean getDrulesBean() throws DrulesNestableException {
        if (drulesBean == null) {
            drulesBean = DrulesParser.parseIntegrityBean(drulesFilename);
        }
        return drulesBean;
    }

    public List<Identifier> listTasks() throws DrulesNestableException {
        final List<TaskBean> taskBeanList = getDrulesBean().getTaskBeanList();
        final List<Identifier> identifierList = new ArrayList<>(taskBeanList.size());
        for (final TaskBean taskBean : taskBeanList) {
            identifierList.add(taskBean.getId());
        }
        return identifierList;
    }
}