org.openengsb.connector.script.internal.ScriptServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.openengsb.connector.script.internal.ScriptServiceImpl.java

Source

/**
 * Licensed to the Austrian Association for Software Tool Integration (AASTI)
 * under one or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership. The AASTI 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.openengsb.connector.script.internal;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.apache.commons.lang.exception.ExceptionUtils;
import org.openengsb.core.api.AliveState;
import org.openengsb.core.api.context.ContextCurrentService;
import org.openengsb.core.api.context.ContextHolder;
import org.openengsb.core.api.model.OpenEngSBFileModel;
import org.openengsb.core.common.AbstractOpenEngSBConnectorService;
import org.openengsb.core.common.util.ModelUtils;
import org.openengsb.domain.build.BuildDomain;
import org.openengsb.domain.build.BuildDomainEvents;
import org.openengsb.domain.build.BuildFailEvent;
import org.openengsb.domain.build.BuildStartEvent;
import org.openengsb.domain.build.BuildSuccessEvent;
import org.openengsb.domain.deploy.DeployDomain;
import org.openengsb.domain.deploy.DeployDomainEvents;
import org.openengsb.domain.deploy.DeployFailEvent;
import org.openengsb.domain.deploy.DeployStartEvent;
import org.openengsb.domain.deploy.DeploySuccessEvent;
import org.openengsb.domain.test.TestDomain;
import org.openengsb.domain.test.TestDomainEvents;
import org.openengsb.domain.test.TestFailEvent;
import org.openengsb.domain.test.TestStartEvent;
import org.openengsb.domain.test.TestSuccessEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ScriptServiceImpl extends AbstractOpenEngSBConnectorService
        implements BuildDomain, TestDomain, DeployDomain {

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

    private static final int MAX_LOG_FILES = 5;

    private BuildDomainEvents buildEvents;
    private TestDomainEvents testEvents;
    private DeployDomainEvents deployEvents;

    private Executor executor = Executors.newSingleThreadExecutor();
    private ExecutorService outputReaderPool = Executors.newCachedThreadPool();

    private boolean synchronous = false;

    private boolean useLogFile = true;

    private ContextCurrentService contextService;

    private String command;
    private String parameters;
    private File logDir;

    public ScriptServiceImpl(String id) {
        super(id);
        String karafData = System.getProperty("karaf.data");
        logDir = new File(karafData, "log");
        if (!logDir.exists()) {
            logDir.mkdir();
        } else if (!logDir.isDirectory()) {
            throw new IllegalStateException("cannot access log-directory");
        }
    }

    @Override
    public AliveState getAliveState() {
        return AliveState.ONLINE;
    }

    @Override
    public String runTests(final OpenEngSBFileModel path) {
        final String id = createId();
        final String contextId = ContextHolder.get().getCurrentContextId();
        Runnable runTests = new Runnable() {

            @Override
            public void run() {
                ContextHolder.get().setCurrentContextId(contextId);
                ScriptResult result = excuteCommand(path.getFile());
                testEvents.raiseTestStartEvent(new TestStartEvent(id));
                if (result.isSuccess()) {
                    OpenEngSBFileModel outPath = ModelUtils.createEmptyModelObject(OpenEngSBFileModel.class);
                    outPath.setFile(path.getFile());
                    testEvents.raiseTestSuccessEvent(new TestSuccessEvent(id, result.getOutput(), outPath));
                } else {
                    testEvents.raiseTestFailEvent(new TestFailEvent(id, result.getOutput()));
                }
            }
        };
        execute(runTests);
        return id;
    }

    @Override
    public void runTestsProcessId(final OpenEngSBFileModel path, final long processId) {
        final String contextId = ContextHolder.get().getCurrentContextId();
        Runnable runTests = new Runnable() {
            @Override
            public void run() {
                ContextHolder.get().setCurrentContextId(contextId);
                ScriptResult result = excuteCommand(path.getFile());
                testEvents.raiseTestStartEvent(new TestStartEvent(processId));
                if (result.isSuccess()) {
                    OpenEngSBFileModel outPath = ModelUtils.createEmptyModelObject(OpenEngSBFileModel.class);
                    outPath.setFile(path.getFile());
                    testEvents.raiseTestSuccessEvent(new TestSuccessEvent(processId, result.getOutput(), outPath));
                } else {
                    testEvents.raiseTestFailEvent(new TestFailEvent(processId, result.getOutput()));
                }
            }
        };
        execute(runTests);
    }

    @Override
    public String build(final OpenEngSBFileModel path) {
        final String id = createId();
        final String contextId = ContextHolder.get().getCurrentContextId();
        Runnable doBuild = new Runnable() {
            @Override
            public void run() {
                ContextHolder.get().setCurrentContextId(contextId);
                ScriptResult result = excuteCommand(path.getFile());
                buildEvents.raiseEvent(new BuildStartEvent(id));
                if (result.isSuccess()) {
                    OpenEngSBFileModel outPath = ModelUtils.createEmptyModelObject(OpenEngSBFileModel.class);
                    outPath.setFile(path.getFile());
                    buildEvents.raiseEvent(new BuildSuccessEvent(id, result.getOutput(), outPath));
                } else {
                    buildEvents.raiseEvent(new BuildFailEvent(id, result.getOutput()));
                }
            }
        };
        execute(doBuild);
        return id;
    }

    @Override
    public void build(final OpenEngSBFileModel path, final long processId) {
        final String contextId = ContextHolder.get().getCurrentContextId();
        Runnable doBuild = new Runnable() {
            @Override
            public void run() {
                ContextHolder.get().setCurrentContextId(contextId);
                ScriptResult result = excuteCommand(path.getFile());
                BuildStartEvent buildStartEvent = new BuildStartEvent();
                buildStartEvent.setProcessId(processId);
                buildEvents.raiseEvent(buildStartEvent);
                if (result.isSuccess()) {
                    OpenEngSBFileModel outPath = ModelUtils.createEmptyModelObject(OpenEngSBFileModel.class);
                    outPath.setFile(path.getFile());
                    buildEvents.raiseEvent(new BuildSuccessEvent(processId, result.getOutput(), outPath));
                } else {
                    buildEvents.raiseEvent(new BuildFailEvent(processId, result.getOutput()));
                }
            }
        };
        execute(doBuild);

    }

    private void execute(Runnable runnable) {
        if (synchronous) {
            runnable.run();
        } else {
            executor.execute(runnable);
        }
    }

    @Override
    public String deploy(final OpenEngSBFileModel path) {
        final String id = createId();
        final String contextId = ContextHolder.get().getCurrentContextId();
        Runnable doDeploy = new Runnable() {

            @Override
            public void run() {
                ContextHolder.get().setCurrentContextId(contextId);
                ScriptResult result = excuteCommand(path.getFile());
                deployEvents.raiseEvent(new DeployStartEvent(id));
                if (result.isSuccess()) {
                    String location = "TODO";
                    deployEvents.raiseEvent(new DeploySuccessEvent(id, result.getOutput(), location));
                } else {
                    deployEvents.raiseEvent(new DeployFailEvent(id, result.getOutput()));
                }
            }
        };
        execute(doDeploy);
        return id;
    }

    @Override
    public void deploy(final OpenEngSBFileModel path, final long processId) {
        final String contextId = ContextHolder.get().getCurrentContextId();
        Runnable doDeploy = new Runnable() {
            @Override
            public void run() {
                ContextHolder.get().setCurrentContextId(contextId);
                ScriptResult result = excuteCommand(path.getFile());
                deployEvents.raiseEvent(new DeployStartEvent(processId));
                if (result.isSuccess()) {
                    String location = "TODO";
                    deployEvents.raiseEvent(new DeploySuccessEvent(processId, result.getOutput(), location));
                } else {
                    deployEvents.raiseEvent(new DeployFailEvent(processId, result.getOutput()));
                }

            }
        };
        execute(doDeploy);
    }

    private String createId() {
        return UUID.randomUUID().toString();
    }

    private synchronized ScriptResult excuteCommand(File dir) {
        List<String> commandList = new ArrayList<String>();
        commandList.add(command);
        if (parameters != null) {
            commandList.addAll(Arrays.asList(parameters.trim().split(" ")));
        }

        try {
            return runCommand(dir, commandList);
        } catch (IOException e) {
            LOGGER.error(e.getMessage(), e);
            return new ScriptResult(false, e.getMessage());
        } catch (InterruptedException e) {
            LOGGER.error(e.getMessage(), e);
            return new ScriptResult(false, e.getMessage());
        }
    }

    private ScriptResult runCommand(File dir, List<String> command) throws IOException, InterruptedException {
        LOGGER.info("running '{}' in directory '{}'", command, dir.getPath());
        Process process = configureProcess(dir, command);
        Future<String> outputFuture = configureProcessOutputReader(process);
        Future<String> errorFuture = configureProcessErrorReader(process);
        boolean processResultCode = process.waitFor() == 0;
        String outputResult = readResultFromFuture(outputFuture);
        String errorResult = readResultFromFuture(errorFuture);
        if (!errorResult.isEmpty()) {
            LOGGER.warn("Maven connector error stream output: {}", errorResult);
        }
        LOGGER.info("maven exited with status {}", processResultCode);
        return new ScriptResult(processResultCode, outputResult);
    }

    private Process configureProcess(File dir, List<String> command) throws IOException {
        Process process;
        try {
            ProcessBuilder builder = new ProcessBuilder(command);
            process = builder.directory(dir).start();
        } catch (IOException e) {
            /* Try again, relative to the karaf.data directory */
            String newCmd = command.get(0);
            newCmd = new File(System.getProperty("karaf.data")).getAbsolutePath() + File.separator + newCmd;
            command.remove(0);
            command.add(0, newCmd);

            ProcessBuilder builder = new ProcessBuilder(command);
            process = builder.directory(dir).start();
        }
        return process;
    }

    private Future<String> configureProcessErrorReader(Process process) {
        ProcessOutputReader error = new ProcessOutputReader(process.getErrorStream());
        return outputReaderPool.submit(error);
    }

    private Future<String> configureProcessOutputReader(Process process) throws IOException {
        ProcessOutputReader output;
        if (useLogFile) {
            File logFile = getNewLogFile();
            output = new ProcessOutputReader(process.getInputStream(), logFile);
        } else {
            output = new ProcessOutputReader(process.getInputStream());
        }
        return outputReaderPool.submit(output);
    }

    private String readResultFromFuture(Future<String> future) throws InterruptedException {
        String result;
        try {
            result = future.get();
        } catch (ExecutionException e) {
            LOGGER.error(e.getMessage(), e.getCause());
            result = ExceptionUtils.getFullStackTrace(e);
        }
        return result;
    }

    private File getNewLogFile() throws IOException {
        if (logDir.list().length + 1 > MAX_LOG_FILES) {
            assertLogLimit();
        }
        String dateString = new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss").format(new Date());
        String fileName = String.format("maven.%s.log", dateString);
        File logFile = new File(logDir, fileName);
        logFile.createNewFile();
        return logFile;
    }

    private boolean assertLogLimit() {
        File[] logFiles = logDir.listFiles();
        Arrays.sort(logFiles, new Comparator<File>() {
            @Override
            public int compare(File f1, File f2) {
                return Long.valueOf(f1.lastModified()).compareTo(f2.lastModified());
            }
        });
        return logFiles[0].delete();
    }

    public void setBuildEvents(BuildDomainEvents buildEvents) {
        this.buildEvents = buildEvents;
    }

    public void setTestEvents(TestDomainEvents testEvents) {
        this.testEvents = testEvents;
    }

    public void setDeployEvents(DeployDomainEvents deployEvents) {
        this.deployEvents = deployEvents;
    }

    public void setContextService(ContextCurrentService contextService) {
        this.contextService = contextService;
    }

    protected BuildDomainEvents getBuildEvents() {
        return buildEvents;
    }

    protected TestDomainEvents getTestEvents() {
        return testEvents;
    }

    protected DeployDomainEvents getDeployEvents() {
        return deployEvents;
    }

    protected ContextCurrentService getContextService() {
        return contextService;
    }

    public void setSynchronous(boolean synchronous) {
        this.synchronous = synchronous;
    }

    public boolean isSynchronous() {
        return synchronous;
    }

    public void setCommand(String command) {
        this.command = command;
    }

    public void setUseLogFile(boolean useLogFile) {
        this.useLogFile = useLogFile;
    }

    public int getLogLimit() {
        return MAX_LOG_FILES;
    }

    public void setParameters(String parameters) {
        this.parameters = parameters;
    }

    public String getParameters() {
        return parameters;
    }

    private class ScriptResult {
        private String output;

        private boolean success;

        public ScriptResult(boolean success, String output) {
            this.success = success;
            this.output = output;
        }

        public String getOutput() {
            return output;
        }

        public boolean isSuccess() {
            return success;
        }
    }

}