com.amazonaws.eclipse.dynamodb.testtool.TestToolProcess.java Source code

Java tutorial

Introduction

Here is the source code for com.amazonaws.eclipse.dynamodb.testtool.TestToolProcess.java

Source

/*
 * Copyright 2013 Amazon Technologies, Inc.
 *
 * 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://aws.amazon.com/apache2.0
 *
 * This file 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 com.amazonaws.eclipse.dynamodb.testtool;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Collection;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.AbstractFileFilter;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.console.ConsolePlugin;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.console.IConsoleConstants;
import org.eclipse.ui.console.IConsoleManager;
import org.eclipse.ui.console.IConsoleView;
import org.eclipse.ui.console.MessageConsole;
import org.eclipse.ui.console.MessageConsoleStream;

import com.amazonaws.eclipse.core.AwsToolkitCore;
import com.amazonaws.eclipse.dynamodb.DynamoDBPlugin;

/**
 * A DynamoDB Local Test Tool process.
 */
public class TestToolProcess {

    private final IVMInstall jre;
    private final File installDirectory;
    private final int port;

    private final Thread shutdownHook = new Thread() {
        @Override
        public void run() {
            TestToolProcess.this.stopProcess();
        }
    };

    private Process process;

    /**
     * Create a new {@code TestToolProcess}.
     *
     * @param jre               the JRE to use to run DynamoDBLocal
     * @param installDirectory  the root install directory for DynamoDBLocal
     * @param port              the TCP port to bind to
     */
    public TestToolProcess(final IVMInstall jre, final File installDirectory, final int port) {
        this.jre = jre;
        this.installDirectory = installDirectory;
        this.port = port;
    }

    /**
     * @return the port that this DynamoDBLocal process is listening on
     */
    public int getPort() {
        return port;
    }

    /**
     * Start the DynamoDBLocal process.
     *
     * @param  onExitAction optional action to be executed when the process
     *                      exits
     * @throws IOException  if starting the process fails
     */
    public synchronized void start(final Runnable onExitAction) throws IOException {

        if (process != null) {
            throw new IllegalStateException("Already started!");
        }

        ProcessBuilder builder = new ProcessBuilder();

        builder.directory(installDirectory);
        builder.command(jre.getInstallLocation().getAbsolutePath().concat("/bin/java"),
                "-Djava.library.path=".concat(findLibraryDirectory().getAbsolutePath()), "-jar",
                "DynamoDBLocal.jar", "--port", Integer.toString(port));

        // Drop STDERR into STDOUT so we can handle them together.
        builder.redirectErrorStream(true);

        // Register a shutdown hook to kill DynamoDBLocal if Eclipse exits.
        Runtime.getRuntime().addShutdownHook(shutdownHook);

        // Start the DynamoDBLocal process.
        process = builder.start();

        // Start a background thread to read any output from DynamoDBLocal
        // and dump it to an IConsole.
        new ConsoleOutputLogger(process.getInputStream(), onExitAction).start();
    }

    /**
     * Searches within the install directory for the native libraries required
     * by DyanmoDB Local (i.e. SQLite) and returns the directory containing the
     * native libraries.
     *
     * @return The directory within the install directory where native libraries
     *         were found; otherwise, if no native libraries are found, the
     *         install directory is returned.
     */
    private File findLibraryDirectory() {
        // Mac and Linux libraries start with "libsqlite4java-" so
        // use that pattern to identify the library directory
        IOFileFilter fileFilter = new AbstractFileFilter() {
            public boolean accept(File dir, String name) {
                return name.startsWith("libsqlite4java-");
            }
        };

        Collection<File> files = FileUtils.listFiles(installDirectory, fileFilter, TrueFileFilter.INSTANCE);

        // Log a warning if we can't identify the library directory,
        // and then just try to use the install directory
        if (files == null || files.isEmpty()) {
            Status status = new Status(IStatus.WARNING, DynamoDBPlugin.PLUGIN_ID,
                    "Unable to find DynamoDB Local native libraries in " + installDirectory);
            AwsToolkitCore.getDefault().getLog().log(status);
            return installDirectory;
        }

        return files.iterator().next().getParentFile();
    }

    /**
     * Stop the DynamoDBLocal process and deregister the shutdown hook.
     */
    public synchronized void stop() {
        stopProcess();
        Runtime.getRuntime().removeShutdownHook(shutdownHook);
    }

    /**
     * Internal helper method to actually stop the DynamoDBLocal process.
     */
    private void stopProcess() {
        if (process != null) {
            process.destroy();
            process = null;
        }
    }

    /**
     * A background thread that reads output from the DynamoDBLocal process
     * and pumps it to a console window.
     */
    private static class ConsoleOutputLogger extends Thread {

        private final BufferedReader input;
        private final Runnable onExitAction;

        /**
         * Create a new logger.
         *
         * @param stream        the input stream to read from
         * @param onExitaction  an optional action to be run when the process
         *                      exits
         */
        public ConsoleOutputLogger(final InputStream stream, final Runnable onExitAction) {

            this.input = new BufferedReader(new InputStreamReader(stream));
            this.onExitAction = onExitAction;

            super.setDaemon(true);
            super.setName("DynamoDBLocal Console Output Logger");
        }

        @Override
        public void run() {
            MessageConsole console = findConsole();
            displayConsole(console);

            MessageConsoleStream stream = console.newMessageStream();

            try {

                while (true) {
                    String line = input.readLine();
                    if (line == null) {
                        stream.println("*** DynamoDB Local Exited ***");
                        break;
                    }

                    stream.println(line);
                }

            } catch (IOException exception) {
                stream.println("Exception reading the output of " + "DynamoDB Local: " + exception.toString());
            } finally {
                try {
                    input.close();
                } catch (IOException exception) {
                    exception.printStackTrace();
                }

                try {
                    stream.close();
                } catch (IOException exception) {
                    exception.printStackTrace();
                }

                if (onExitAction != null) {
                    onExitAction.run();
                }
            }
        }

        /**
         * Find or create a new console window for DynamoDBLocal's output.
         *
         * @return  the console to log output to
         */
        private MessageConsole findConsole() {
            ConsolePlugin plugin = ConsolePlugin.getDefault();
            IConsoleManager manager = plugin.getConsoleManager();

            IConsole[] existing = manager.getConsoles();
            for (int i = 0; i < existing.length; ++i) {
                if (existing[i].getName().equals("DynamoDB Local")) {
                    return (MessageConsole) existing[i];
                }
            }

            MessageConsole console = new MessageConsole("DynamoDB Local", null);
            manager.addConsoles(new IConsole[] { console });

            return console;
        }

        /**
         * Display the given console if it's not already visible.
         *
         * @param console   the console to display
         */
        private void displayConsole(final IConsole console) {
            Display.getDefault().asyncExec(new Runnable() {
                public void run() {
                    IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();

                    try {

                        IConsoleView view = (IConsoleView) page.showView(IConsoleConstants.ID_CONSOLE_VIEW);

                        view.display(console);

                    } catch (PartInitException exception) {
                        // Is there something more intelligent we should be
                        // doing with this?
                        exception.printStackTrace();
                    }
                }
            });
        }
    }
}