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

org.craftercms.commons.mongo.MongoScriptRunner Maven / Gradle / Ivy

There is a newer version: 4.2.0
Show newest version
/*
 * Copyright (C) 2007-2020 Crafter Software Corporation. All Rights Reserved.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
package org.craftercms.commons.mongo;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.annotation.PostConstruct;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import com.mongodb.CommandResult;
import com.mongodb.DB;
import com.mongodb.Mongo;
import com.mongodb.MongoClientURI;

/**
 * Utility class for running Mongo scripts in JS.
 *
 * @author avasquez
 */
@SuppressWarnings("deprecation")
public class MongoScriptRunner {

    private static final Logger logger = LoggerFactory.getLogger(MongoScriptRunner.class);

    private Mongo mongo;
    private String dbName;
    private String username;
    private String password;
    private List scriptPaths;
    private boolean runOnInit;
    private boolean useMongoClient;
    private String mongoClientBin;
    private String connectionStr;


    public MongoScriptRunner() {
        this.runOnInit = true;
    }

    @Required
    public void setMongo(Mongo mongo) {
        this.mongo = mongo;
    }

    @Required
    public void setDbName(String dbName) {
        this.dbName = dbName;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Required
    public void setScriptPaths(List scriptPaths) {
        this.scriptPaths = scriptPaths;
    }

    public void setRunOnInit(boolean runOnInit) {
        this.runOnInit = runOnInit;
    }

    public void setUseMongoClient(final boolean useMongoClient) {
        this.useMongoClient = useMongoClient;
    }

    public void setMongoClientBin(final String mongoClientBin) {
        this.mongoClientBin = mongoClientBin;
    }

    public void setConnectionStr(final String connectionStr) {
        this.connectionStr = connectionStr;
    }

    @PostConstruct
    public void init()  {
        logger.debug("Running Scripts?",runOnInit);
        if (runOnInit) {
            logger.debug("Using Mongo Client",useMongoClient);
            if(useMongoClient){
                runScriptsWithMongoClient();
            }else {
                runScripts();
            }
        }
    }

    private void runScriptsWithMongoClient() {
        List toExecute=new ArrayList<>();

        try {
            for (Resource scriptPath : scriptPaths) {
                if (scriptPath.getFile().isDirectory()) {
                    Files.walkFileTree(scriptPath.getFile().toPath(), new JSFileVisitor(toExecute));
                }else{
                    toExecute.add(scriptPath.getFile().getAbsolutePath());
                }
            }
            final Path allScripsFile = Files.createTempFile("ScriptRunner", ".js");
            StringBuilder builder =new StringBuilder();
            for (String path : toExecute) {
                builder.append(String.format("load('%s');\n",path));
            }
            FileUtils.writeStringToFile(allScripsFile.toFile(),builder.toString(),"UTF-8");
            runScript(allScripsFile);
            Files.deleteIfExists(allScripsFile);
        }catch (IOException | MongoDataException ex){
            logger.error("Unable to run script using MongoClient",ex);
        }
    }

    public void runScripts() {
        try {
            DB db = getDB();
            logger.debug("Running Scriptns in {}",db.getName());
            for (Resource scriptPath : scriptPaths) {
                runScript(db, scriptPath);
            }
        }catch (Exception ex){
            //We where unable to run script due some reason , logged and make sure the app at least startup.
            logger.error("Unable to run scripts due a internal exception ",ex);
        }
    }

    private void runScript(DB db, Resource scriptPath) throws MongoDataException {
        String script;
        try {
            if (scriptPath.getFile().isDirectory()) {
                final File[] files = scriptPath.getFile().listFiles(new FilenameFilter() {
                    @Override
                    public boolean accept(final File dir, final String name) {
                        return name.toLowerCase().endsWith(".js");
                    }
                });
                List orderFiles = Arrays.asList(files);
                Collections.sort(orderFiles, new Comparator() {
                    @Override
                    public int compare(final File o1, final File o2) {
                        return o1.getName().compareTo(o2.getName());
                    }
                });
                logger.debug("Directory {} files to exec {}",scriptPath.getFile(),orderFiles);
                for (File file : orderFiles) {
                    runScript(db, new FileSystemResource(file.getPath()));
                }
            } else {
                logger.debug("Running Script {}",scriptPath.getURI());
                try {
                    script = IOUtils.toString(scriptPath.getInputStream(), "UTF-8");
                } catch (IOException e) {
                    throw new MongoDataException("Unable to read script at " + scriptPath.getURI().toString());
                }

                CommandResult result = db.doEval(script);
                if (!result.ok()) {
                    Exception ex = result.getException();

                    throw new MongoDataException("An error occurred while running script at " + scriptPath.getURI().toString(),
                        ex);

                }
                logger.info("Mongo script at {} executed successfully", scriptPath.getDescription());
            }
        } catch (IOException ex) {
            logger.error("Unable to read files from {}", ex);
        }
    }

    private void runScript(Path scriptPath) throws MongoDataException {
        String script;
        try {
            ProcessBuilder mongoProcess = new ProcessBuilder(getCommands(scriptPath));
            Process process=mongoProcess.start();
            int result=process.waitFor();
            String stdOut;
            String errOut;
            try(ByteArrayOutputStream out = new ByteArrayOutputStream()){
                IOUtils.copy(process.getInputStream(),out);
                stdOut = new String(out.toByteArray(),"UTF-8");
                out.reset();
                IOUtils.copy(process.getErrorStream(),out);
                errOut = new String(out.toByteArray(),"UTF-8");
                IOUtils.copy(process.getInputStream(),out);
            }
            if(result!=0){
                throw new IOException("Process return error \n std out:"+stdOut+"\n err out: \n"+errOut);
            }else{
             logger.debug("Process return \n std out:"+stdOut+"\n err out: \n"+errOut);
            }
        } catch (IOException | InterruptedException ex) {
            logger.error("Unable to Execute mongo Process", ex);
        }
    }

    private List getCommands(final Path scriptPath) throws MongoDataException {
        List commandList=new ArrayList<>();
        if(SystemUtils.IS_OS_WINDOWS){
            commandList.add("CMD");
            commandList.add("/C");
        }
        if(StringUtils.isBlank(mongoClientBin)){
            throw new MongoDataException("Unable to run scripts, mongo client bin path is not set ");
        }
        String pwd = null;
        String authSource=null;
        String user=null;
        MongoClientURI uri=new MongoClientURI(connectionStr);
        if(uri.getCredentials()!=null) {
             authSource= uri.getCredentials().getSource();
             user= uri.getCredentials().getUserName();
            if (uri.getCredentials().getPassword() != null) {
                pwd = new String(uri.getCredentials().getPassword());
            }
        }
        String replicaSetName="";
        if(uri.getHosts().size()>1){
            replicaSetName=uri.getOptions().getRequiredReplicaSetName()+"/";
        }
        final String host = StringUtils.trim(replicaSetName+StringUtils.join(uri.getHosts(),","));
        commandList.add(mongoClientBin);
        commandList.add("--host");
        commandList.add(host);
        commandList.add(uri.getDatabase());
        if(StringUtils.isNotBlank(user) && StringUtils.isNotBlank(pwd) && StringUtils.isNotBlank(authSource)) {
            commandList.add("-u");
            commandList.add(user);
            commandList.add("-p");
            commandList.add(pwd);
            commandList.add("--authenticationDatabase");
            commandList.add(authSource);
        }
        commandList.add(scriptPath.toAbsolutePath().toString());
        return commandList;
    }

    private DB getDB() throws MongoDataException {
        DB db = mongo.getDB(dbName);
        logger.debug("Getting DB {}",dbName);
        return db;
    }


    class JSFileVisitor extends SimpleFileVisitor {
        private List filesTOAdd;

        public JSFileVisitor(final List filesTOAdd) {
            this.filesTOAdd = filesTOAdd;
        }

        @Override
        public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
            if(attrs.isRegularFile() && attrs.size()>0 && StringUtils.endsWithIgnoreCase(file.toString(),".js")){
                filesTOAdd.add(file.toAbsolutePath().toString());
            }
            return super.visitFile(file, attrs);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy