All Downloads are FREE. Search and download functionalities are using the official Maven repository.

info.archinnov.achilles.script.ScriptExecutor Maven / Gradle / Ivy

There is a newer version: 6.1.0
Show newest version
package info.archinnov.achilles.script;


import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.SimpleStatement;
import com.datastax.driver.core.Statement;
import com.google.common.util.concurrent.MoreExecutors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.InputStream;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static info.archinnov.achilles.internals.statement.StatementHelper.isDMLStatement;
import static info.archinnov.achilles.logger.AchillesLoggers.ACHILLES_DDL_SCRIPT;
import static info.archinnov.achilles.logger.AchillesLoggers.ACHILLES_DML_STATEMENT;
import static org.apache.commons.lang3.StringUtils.isNotBlank;

import info.archinnov.achilles.internals.futures.FutureUtils;
import info.archinnov.achilles.validation.Validator;

/**
 * Facility class to execute a CQL script file or a plain CQL statement
 */
public class ScriptExecutor {

    private static final Logger DML_LOGGER = LoggerFactory.getLogger(ACHILLES_DML_STATEMENT);
    private static final Logger DDL_LOGGER = LoggerFactory.getLogger(ACHILLES_DDL_SCRIPT);

    private static final String COMMA = ";";
    private static final String BATCH_BEGIN = "BEGIN";
    private static final String BATCH_APPLY = "APPLY";

    private static final String CODE_DELIMITER_START = "^\\s*(?:AS)?\\s*\\$\\$\\s*$";
    private static final String CODE_DELIMITER_END = "^\\s*\\$\\$\\s*;\\s*$";
    private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\$\\{([a-z][a-zA-Z0-9_]*)\\}");

    private static final Map EMPTY_MAP = new HashMap<>();

    private final ExecutorService sameThreadExecutor = MoreExecutors.newDirectExecutorService();
    private final Session session;

    public ScriptExecutor(Session session) {
        this.session = session;
    }

    /**
     * Execute a CQL script file located in the class path
     *
     * @param scriptLocation the location of the script file in the class path
     */
    public void executeScript(String scriptLocation) {
        executeScriptTemplate(scriptLocation, EMPTY_MAP);
    }

    /**
     * Execute a CQL script template located in the class path and
     * inject provided values into the template to produce the actual script
     *
     * @param scriptTemplateLocation the location of the script template in the class path
     * @param values                 template values
     */
    public void executeScriptTemplate(String scriptTemplateLocation, Map values) {
        final List statements = buildStatements(loadScriptAsLines(scriptTemplateLocation, values));
        for (SimpleStatement statement : statements) {
            if (isDMLStatement(statement)) {
                DML_LOGGER.debug("\tSCRIPT : {}", statement.getQueryString());
            } else {
                DDL_LOGGER.debug("\t\tSCRIPT : {}", statement.getQueryString());
            }
            session.execute(statement);
        }
    }

    /**
     * Execute a plain CQL string statement
     * @param statement
     *      plain CQL string statement
     *
     * @return the resultSet
     *
     */
    public ResultSet execute(String statement) {
        return session.execute(statement);
    }

    /**
     * Execute a CQL statement
     * @param statement
     *      CQL statement
     *
     * @return the resultSet
     *
     */
    public ResultSet execute(Statement statement) {
        return session.execute(statement);
    }

    /**
     * Execute a plain CQL string statement asynchronously
     *
     * @param statement the CQL string statement
     * @return CompletableFuture<ResultSet>
     */
    public CompletableFuture executeAsync(String statement) {
        return FutureUtils.toCompletableFuture(session.executeAsync(statement), sameThreadExecutor);
    }

    /**
     * Execute a CQL statement asynchronously
     *
     * @param statement CQL statement
     * @return CompletableFuture<ResultSet>
     */
    public CompletableFuture executeAsync(Statement statement) {
        return FutureUtils.toCompletableFuture(session.executeAsync(statement), sameThreadExecutor);
    }

    protected List loadScriptAsLines(String scriptLocation) {
        return loadScriptAsLines(scriptLocation, EMPTY_MAP);
    }

    protected List loadScriptAsLines(String scriptLocation, Map variables) {

        InputStream inputStream = this.getClass().getResourceAsStream("/" + scriptLocation);

        Validator.validateNotNull(inputStream, "Cannot find CQL script file at location '%s'", scriptLocation);

        Scanner scanner = new Scanner(inputStream);
        List lines = new ArrayList<>();
        while (scanner.hasNextLine()) {
            String nextLine = maybeReplaceVariables(scanner, variables);
            if (isNotBlank(nextLine)) {
                lines.add(nextLine);
            }
        }
        return lines;
    }

    private String maybeReplaceVariables(Scanner scanner, Map variables) {
        String nextLine = scanner.nextLine().trim();
        if (isNotBlank(nextLine) && !variables.isEmpty()) {
            final Matcher matcher = VARIABLE_PATTERN.matcher(nextLine);
            while (matcher.find()) {
                final String group = matcher.group(1);
                Validator.validateTrue(variables.containsKey(group),
                        "Cannot find value for variable ${%s} in the variable map provided to ScriptExecutor", group);
                nextLine = nextLine.replaceFirst("\\$\\{" + group + "\\}", variables.get(group).toString());

            }
        }
        return nextLine;
    }

protected List buildStatements(List lines) {
        List statements = new ArrayList<>();
        StringBuilder statement = new StringBuilder();
        boolean batch = false;
        boolean codeBlock = false;
        StringBuilder batchStatement = new StringBuilder();
        for (String line : lines) {
            if (line.trim().startsWith(BATCH_BEGIN)) {
                batch = true;
            }
            if (line.trim().matches(CODE_DELIMITER_START)) {
                if(codeBlock) {
                    codeBlock = false;
                } else {
                    codeBlock = true;
                }
            }

            if (batch) {
                batchStatement.append(line);
                if (line.trim().startsWith(BATCH_APPLY)) {
                    batch = false;
                    statements.add(new SimpleStatement(batchStatement.toString()));
                    batchStatement = new StringBuilder();
                }
            } else if(codeBlock) {
                statement.append(line);
                if (line.trim().matches(CODE_DELIMITER_END)) {
                    codeBlock = false;
                    statements.add(new SimpleStatement(statement.toString()));
                    statement = new StringBuilder();
                }
            }
            else {
                statement.append(line);
                if (line.trim().endsWith(COMMA)) {
                    statements.add(new SimpleStatement(statement.toString()));
                    statement = new StringBuilder();
                } else {
                    statement.append(" ");
                }
            }
        }
        return statements;
    }

    public Session getSession() {
        return session;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy