org.wisdom.maven.utils.WisdomExecutor.java Source code

Java tutorial

Introduction

Here is the source code for org.wisdom.maven.utils.WisdomExecutor.java

Source

/*
 * #%L
 * Wisdom-Framework
 * %%
 * Copyright (C) 2013 - 2014 Wisdom Framework
 * %%
 * 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.
 * #L%
 */
package org.wisdom.maven.utils;

import com.google.common.base.Strings;
import org.apache.commons.exec.*;
import org.apache.commons.io.FileUtils;
import org.apache.maven.plugin.MojoExecutionException;
import org.wisdom.maven.mojos.AbstractWisdomMojo;

import java.io.File;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Properties;

/**
 * Launch the Wisdom Framework.
 * This class starts the Wisdom Framework. It handles background (start and stop) and foreground executions.
 */
public class WisdomExecutor {

    /**
     * Retrieves the version of Chameleon we use.
     */
    public static final String CHAMELEON_VERSION = BuildConstants.get("CHAMELEON_VERSION");

    /**
     * The maximum time to wait in ms for file creation and deletion.
     */
    public static final int FILE_WAIT_TIMEOUT = 10000;

    /**
     * Launches the Wisdom server in background using the {@literal chameleon.sh} scripts.
     * This method works only on Linux and Mac OS X.
     *
     * @param mojo the mojo
     * @throws MojoExecutionException if the Wisdom instance cannot be started.
     */
    public void executeInBackground(AbstractWisdomMojo mojo) throws MojoExecutionException {
        File script = new File(mojo.getWisdomRootDirectory(), "chameleon.sh");
        if (!script.isFile()) {
            throw new MojoExecutionException("The 'chameleon.sh' file does not exist in "
                    + mojo.getWisdomRootDirectory().getAbsolutePath() + " - cannot launch Wisdom");
        }

        // Remove the RUNNING_PID file if exists.
        File pid = new File(mojo.getWisdomRootDirectory(), "RUNNING_PID");
        if (pid.isFile()) {
            mojo.getLog().info("The RUNNING_PID file is present, deleting it");
            FileUtils.deleteQuietly(pid);
        }

        // We create a command line, as we are using th toString method on it.
        CommandLine cmdLine = new CommandLine(script);
        appendSystemPropertiesToCommandLine(mojo, cmdLine);

        try {
            mojo.getLog().info("Launching Wisdom Server using '" + cmdLine.toString() + "'.");
            // As we know which command line we are executing, we can safely execute the command.
            Runtime.getRuntime().exec(cmdLine.toStrings(), null, mojo.getWisdomRootDirectory()); //NOSONAR see comment
        } catch (IOException e) {
            throw new MojoExecutionException("Cannot execute Wisdom", e);
        }
    }

    /**
     * Launches the Wisdom server. This method blocks until the wisdom server shuts down.
     * It uses the {@literal Java} executable directly.
     *
     * @param mojo        the mojo
     * @param interactive enables the shell prompt
     * @param debug       the debug port (0 to disable it)
     * @param jvmArgs     JVM arguments to add to the `java` command (before the -jar argument).
     * @param destroyer   a process destroyer that can be used to destroy the process, if {@code null}
     *                    a {@link org.apache.commons.exec.ShutdownHookProcessDestroyer} is used.
     * @throws MojoExecutionException if the Wisdom instance cannot be started or has thrown an unexpected status
     *                                while being stopped.
     */
    public void execute(AbstractWisdomMojo mojo, boolean interactive, int debug, String jvmArgs,
            ProcessDestroyer destroyer) throws MojoExecutionException {
        // Get java
        File java = ExecUtils.find("java", new File(mojo.javaHome, "bin"));
        if (java == null) {
            throw new MojoExecutionException("Cannot find the java executable");
        }

        CommandLine cmdLine = new CommandLine(java);

        if (debug != 0) {
            cmdLine.addArgument("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=" + debug, false);
        }

        if (!Strings.isNullOrEmpty(jvmArgs)) {
            cmdLine.addArguments(jvmArgs, false);
        }

        cmdLine.addArgument("-jar");
        cmdLine.addArgument("bin/chameleon-core-" + CHAMELEON_VERSION + ".jar");
        if (interactive) {
            cmdLine.addArgument("--interactive");
        }

        appendSystemPropertiesToCommandLine(mojo, cmdLine);

        DefaultExecutor executor = new DefaultExecutor();
        if (destroyer != null) {
            executor.setProcessDestroyer(destroyer);
        } else {
            executor.setProcessDestroyer(new ShutdownHookProcessDestroyer());
        }

        executor.setWorkingDirectory(mojo.getWisdomRootDirectory());
        if (interactive) {
            executor.setStreamHandler(new PumpStreamHandler(System.out, System.err, System.in)); //NOSONAR
            // Using the interactive mode the framework should be stopped using the 'exit' command,
            // and produce a '0' status.
            executor.setExitValue(0);
        } else {
            executor.setStreamHandler(new PumpStreamHandler(System.out, System.err)); // NOSONAR
            // As the execution is intended to be interrupted using CTRL+C, the status code returned is expected to be 1
            // 137 or 143 is used when stopped by the destroyer.
            executor.setExitValues(new int[] { 1, 137, 143 });
        }
        try {
            mojo.getLog().info("Launching Wisdom Server");
            mojo.getLog().debug("Command Line: " + cmdLine.toString());
            // The message is different whether or not we are in the interactive mode.
            if (interactive) {
                mojo.getLog().info("You are in interactive mode");
                mojo.getLog().info("Hit 'exit' to shutdown");
            } else {
                mojo.getLog().info("Hit CTRL+C to exit");
            }
            if (debug != 0) {
                mojo.getLog().info("Wisdom launched with remote debugger interface enabled on port " + debug);
            }
            // Block execution until ctrl+c
            executor.execute(cmdLine);
        } catch (IOException e) {
            throw new MojoExecutionException("Cannot execute Wisdom", e);
        }
    }

    /**
     * Appends the properties from the Maven session (user properties) to the command line. As the command line is
     * intended to be a Chameleon process, arguments are passed using the {@literal -Dkey=value} syntax.
     *
     * @param mojo the mojo
     * @param cmd  the command line to extend
     */
    private static void appendSystemPropertiesToCommandLine(AbstractWisdomMojo mojo, CommandLine cmd) {
        Properties userProperties = mojo.session.getUserProperties();
        if (userProperties != null) {
            //noinspection unchecked
            Enumeration<String> names = (Enumeration<String>) userProperties.propertyNames();
            while (names.hasMoreElements()) {
                String name = names.nextElement();
                cmd.addArgument("-D" + name + "=" + userProperties.getProperty(name));
            }
        }
    }

    /**
     * Stops a running instance of wisdom using 'chameleon stop'.
     *
     * @param mojo the mojo
     * @throws MojoExecutionException if the instance cannot be stopped
     */
    public void stop(AbstractWisdomMojo mojo) throws MojoExecutionException {
        File script = new File(mojo.getWisdomRootDirectory(), "chameleon.sh");
        if (!script.isFile()) {
            throw new MojoExecutionException("The 'chameleon.sh' file does not exist in "
                    + mojo.getWisdomRootDirectory().getAbsolutePath() + " - cannot stop the Wisdom instance");
        }

        // If there is a RUNNING_PID file, exit immediately.
        File pid = new File(mojo.getWisdomRootDirectory(), "RUNNING_PID");
        if (!pid.isFile()) {
            mojo.getLog().info("The RUNNING_PID file does not exist, are you sure Wisdom is running ?");
            return;
        }

        CommandLine cmdLine = new CommandLine(script);
        cmdLine.addArgument("stop");

        try {
            mojo.getLog().info("Stopping Wisdom Server using '" + cmdLine.toString() + "'.");
            // As we know which command line we are executing, we can safely execute the command.
            Runtime.getRuntime().exec(cmdLine.toStrings(), null, mojo.getWisdomRootDirectory()); //NOSONAR
        } catch (IOException e) {
            throw new MojoExecutionException("Cannot stop Wisdom", e);
        }
    }

    /**
     * Waits for a file to be created.
     *
     * @param file the file
     * @return {@literal true} if the file was created, {@literal false} otherwise
     */
    public static boolean waitForFile(File file) {
        if (file.isFile()) {
            return true;
        } else {
            // Start waiting 10 seconds maximum
            long timeout = System.currentTimeMillis() + FILE_WAIT_TIMEOUT;
            while (System.currentTimeMillis() <= timeout) {
                sleepQuietly(10);
                if (file.isFile()) {
                    return true;
                }
            }
        }
        // Timeout reached
        return false;
    }

    /**
     * Sleeps for the given amount of time.
     *
     * @param time the time
     */
    private static void sleepQuietly(long time) {
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            // Ignore it.
        }
    }

    /**
     * Wiats for a file to be deleted.
     *
     * @param file the file
     * @return {@literal true} if the file was deleted, {@literal false} otherwise
     */
    public static boolean waitForFileDeletion(File file) {
        if (!file.isFile()) {
            return true;
        } else {
            // Start waiting 10 seconds maximum
            long timeout = System.currentTimeMillis() + FILE_WAIT_TIMEOUT;
            while (System.currentTimeMillis() <= timeout) {
                sleepQuietly(10);
                if (!file.isFile()) {
                    return true;
                }
            }
        }
        // Timeout reached
        return false;
    }

}