org.stem.ExternalNode.java Source code

Java tutorial

Introduction

Here is the source code for org.stem.ExternalNode.java

Source

/*
 * Copyright 2014 Alexey Plotnik
 *
 * 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 org.stem;

import com.google.common.collect.Lists;
import org.apache.commons.exec.*;
import org.apache.commons.exec.util.StringUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.toolchain.Toolchain;
import org.apache.maven.toolchain.ToolchainManager;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.cli.CommandLineUtils;
import org.stem.utils.YamlConfigurator;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;

public class ExternalNode {

    private MavenContext mvnContext;
    private Log log;

    private final File nodeDir;
    private final YamlConfigurator configurator;
    private int mountPointsNumber = 1;

    public ExternalNode(File nodeDir, YamlConfigurator configurator, MavenContext mvnContext, Log log) {
        this.nodeDir = nodeDir;
        this.configurator = configurator;
        this.mvnContext = mvnContext;
        this.log = log;
    }

    public void start() throws IOException, MojoExecutionException {
        CommandLine commandLine = newCommandLine(nodeDir);
        startStemProcess(commandLine, createEnvironmentVars(nodeDir));
    }

    private Map<String, String> createEnvironmentVars(File nodeDir) {
        Map<String, String> env = new HashMap<String, String>();

        try {
            Properties properties = CommandLineUtils.getSystemEnvVars();
            for (Map.Entry prop : properties.entrySet()) {
                env.put((String) prop.getKey(), (String) prop.getValue());
            }
        } catch (IOException e) {
            log.error("Could not assign default system environment variables.", e);
        }
        env.put("stem.config", new File(new File(nodeDir, "conf"), "stem.yaml").getAbsolutePath());
        return env;
    }

    private DefaultExecuteResultHandler startStemProcess(CommandLine commandLine, Map env)
            throws MojoExecutionException {
        try {
            DefaultExecutor exec = new DefaultExecutor();
            DefaultExecuteResultHandler execHandler = new DefaultExecuteResultHandler();
            exec.setWorkingDirectory(nodeDir);
            exec.setProcessDestroyer(new ShutdownHookProcessDestroyer());

            LogOutputStream stdout = new MavenLogOutputStream(log);
            LogOutputStream stderr = new MavenLogOutputStream(log);

            log.debug("Executing command line: " + commandLine);

            PumpStreamHandler streamHandler = new PumpStreamHandler(stdout, stderr);
            streamHandler.start();
            exec.setStreamHandler(streamHandler);

            exec.execute(commandLine, env, execHandler);
            //            try
            //            {
            //                execHandler.waitFor();
            //            }
            //            catch (InterruptedException e)
            //            {
            //                e.printStackTrace();
            //            }
            return execHandler;
        } catch (IOException e) {
            throw new MojoExecutionException("Command execution failed.", e);
        }
    }

    private CommandLine newCommandLine(File nodeDir) throws IOException {
        createStemHome(nodeDir);
        CommandLine cmd = newJavaCommandLine();
        // TODO: add -Xmx, -Dlog4j.configuration, JMX configuration

        cmd.addArgument("-Dstem.config=" + new File(new File(nodeDir, "conf"), "stem.yaml").getAbsolutePath());
        cmd.addArgument("-jar");
        cmd.addArgument(new File(new File(nodeDir, "bin"), "storagenode.jar").getAbsolutePath(), false);

        return cmd;
    }

    private CommandLine newJavaCommandLine() {
        String javaExecPath = null;

        Toolchain toolchain = getToolchain();

        if (toolchain != null) {
            log.info("Toolchain: " + toolchain);
            javaExecPath = toolchain.findTool("java");

        } else if (OS.isFamilyWindows()) {
            String exec = "java.exe";
            String path = System.getenv("PATH");
            if (path != null) {
                for (String elem : StringUtils.split(path, File.pathSeparator)) {
                    File file = new File(elem, exec);
                    if (file.exists()) {
                        javaExecPath = file.getAbsolutePath();
                        break;
                    }
                }
            }

        }

        if (null == javaExecPath) {
            javaExecPath = "java";
        }

        return new CommandLine(javaExecPath);
    }

    private Toolchain getToolchain() {
        Toolchain toolchain = null;

        try {
            ToolchainManager toolchainManager = (ToolchainManager) mvnContext.session.getContainer()
                    .lookup(ToolchainManager.ROLE);
            if (toolchainManager != null) {
                toolchain = toolchainManager.getToolchainFromBuildContext("jdk", mvnContext.session);
            }
        } catch (ComponentLookupException e) {
            //
        }

        return toolchain;
    }

    private void createStemHome(File nodeDir) throws IOException {
        File bin = new File(nodeDir, "bin");
        File conf = new File(nodeDir, "conf");
        File data = new File(nodeDir, "data");
        createDirsClean(Arrays.asList(bin, conf, data));

        List<File> mpDirs = newMountPointDirs(data);
        createDirsClean(mpDirs);
        newConfigYaml(conf, mpDirs);

        File jarFile = new File(bin, "storagenode.jar");
        createNodeJar(jarFile, NodeSupervisor.class.getName(), nodeDir);
    }

    private void createNodeJar(File jarFile, String mainClass, File nodeDir) throws IOException {
        File conf = new File(nodeDir, "conf");

        FileOutputStream fos = null;
        JarOutputStream jos = null;

        try {
            fos = new FileOutputStream(jarFile);
            jos = new JarOutputStream(fos);
            jos.setLevel(JarOutputStream.STORED);
            jos.putNextEntry(new JarEntry("META-INF/MANIFEST.MF"));

            Manifest man = new Manifest();

            StringBuilder cp = new StringBuilder();
            cp.append(new URL(conf.toURI().toASCIIString()).toExternalForm());
            cp.append(' ');

            log.debug("Adding plugin artifact: " + ArtifactUtils.versionlessKey(mvnContext.pluginArtifact)
                    + " to the classpath");
            cp.append(new URL(mvnContext.pluginArtifact.getFile().toURI().toASCIIString()).toExternalForm());
            cp.append(' ');

            log.debug("Adding: " + mvnContext.classesDir + " to the classpath");
            cp.append(new URL(mvnContext.classesDir.toURI().toASCIIString()).toExternalForm());
            cp.append(' ');

            for (Artifact artifact : mvnContext.pluginDependencies) {
                log.info("Adding plugin dependency artifact: " + ArtifactUtils.versionlessKey(artifact)
                        + " to the classpath");
                // NOTE: if File points to a directory, this entry MUST end in '/'.
                cp.append(new URL(artifact.getFile().toURI().toASCIIString()).toExternalForm());
                cp.append(' ');
            }

            man.getMainAttributes().putValue("Manifest-Version", "1.0");
            man.getMainAttributes().putValue("Class-Path", cp.toString().trim());
            man.getMainAttributes().putValue("Main-Class", mainClass);

            man.write(jos);

        } finally {
            IOUtil.close(jos);
            IOUtil.close(fos);
        }
    }

    private void newConfigYaml(File conf, List<File> mpDirs) {
        String[] mpPaths = getMountPointPaths(mpDirs).toArray(new String[mpDirs.size()]);
        configurator.setBlobMountPoints(mpPaths);

        configurator.save(new File(conf, "stem.yaml"));
    }

    private void createDirsClean(List<File> dirs) {
        for (File dir : dirs) {
            if (dir.isFile())
                dir.delete();

            if (!dir.isDirectory())
                dir.mkdirs();
        }
    }

    private List<File> newMountPointDirs(File conf) {
        List<File> dirs = Lists.newArrayList();
        for (int i = 1; i <= mountPointsNumber; i++) {
            File mp = new File(conf, "mountpoint" + (i > 1 ? i : ""));
            dirs.add(mp);
        }
        return dirs;
    }

    private List<String> getMountPointPaths(List<File> files) {
        List<String> list = Lists.newArrayList();
        for (File file : files) {
            list.add(file.getAbsolutePath());
        }
        return list;
    }

    public ExternalNode setMounPointsNumber(int num) {
        if (num < 1 || num > 254) {
            throw new RuntimeException("number of mount points should be in range (1, 254)");
        }

        this.mountPointsNumber = num;
        return this;
    }
}