com.hellblazer.process.impl.JavaProcessImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.hellblazer.process.impl.JavaProcessImpl.java

Source

/** (C) Copyright 2011-2014 Chiral Behaviors, All Rights Reserved
 * 
 * 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 com.hellblazer.process.impl;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;

import javax.management.MBeanServerConnection;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import javax.security.auth.Subject;

import org.apache.commons.io.input.Tailer;
import org.apache.commons.io.input.TailerListener;

import com.hellblazer.process.CannotStopProcessException;
import com.hellblazer.process.JavaProcess;
import com.hellblazer.process.ManagedProcess;
import com.hellblazer.process.NoLocalJmxConnectionException;
import com.sun.tools.attach.AttachNotSupportedException;
import com.sun.tools.attach.VirtualMachine;

/**
 * @author Hal Hildebrand
 * 
 */
public class JavaProcessImpl implements JavaProcess, Cloneable {
    private static final long serialVersionUID = 1L;
    protected List<String> arguments;
    protected File jarFile;
    protected String javaClass;
    protected File javaExecutable;
    protected transient JMXConnector jmxc;
    protected ManagedProcess process;
    protected List<String> vmOptions;

    public JavaProcessImpl(ManagedProcess process) {
        assert process != null;
        this.process = process;
    }

    /* (non-Javadoc)
     * @see com.hellblazer.process.JavaProcess#addArgument(java.lang.String)
     */
    @Override
    public void addArgument(String argument) {
        if (arguments == null) {
            arguments = new ArrayList<String>();
        }
        arguments.add(argument);
    }

    /* (non-Javadoc)
     * @see com.hellblazer.process.JavaProcess#addArguments(java.lang.String[])
     */
    @Override
    public void addArguments(String[] args) {
        if (arguments == null) {
            arguments = new ArrayList<String>();
        }
        for (String arg : args) {
            arguments.add(arg);
        }
    }

    @Override
    public void addCommand(String command) {
        throw new UnsupportedOperationException("Cannot set the command of the process directly");
    }

    /* (non-Javadoc)
     * @see com.hellblazer.process.JavaProcess#addVmOption(java.lang.String)
     */
    @Override
    public void addVmOption(String vmOption) {
        if (vmOptions == null) {
            vmOptions = new ArrayList<String>();
        }
        vmOptions.add(vmOption);
    }

    /* (non-Javadoc)
     * @see com.hellblazer.process.JavaProcess#addVmOptions(java.lang.String[])
     */
    @Override
    public void addVmOptions(String[] vmOpts) {
        if (vmOptions == null) {
            vmOptions = new ArrayList<String>();
        }
        for (String opt : vmOpts) {
            vmOptions.add(opt);
        }
    }

    @Override
    public JavaProcess clone() {
        JavaProcessImpl clone;
        try {
            clone = (JavaProcessImpl) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new IllegalStateException("Clone not supported", e);
        }
        clone.process = process.clone();
        if (arguments != null) {
            clone.arguments = new ArrayList<String>(arguments);
        }
        if (vmOptions != null) {
            clone.vmOptions = new ArrayList<String>(vmOptions);
        }
        clone.jarFile = jarFile;
        clone.javaClass = javaClass;
        return clone;
    }

    @Override
    public JavaProcess configureFrom(ManagedProcess p) {
        if (!(p instanceof JavaProcess)) {
            throw new UnsupportedOperationException("Can only configure from an instance of JavaProcess");
        }
        JavaProcess javaProcess = (JavaProcess) p;
        javaExecutable = javaProcess.getJavaExecutable();
        arguments = javaProcess.getArguments();
        vmOptions = javaProcess.getVmOptions();
        javaClass = javaProcess.getJavaClass();
        jarFile = javaProcess.getJarFile();
        process.setDirectory(javaProcess.getDirectory());
        process.setEnvironment(javaProcess.getEnvironment());
        return this;
    }

    @Override
    public void destroy() throws CannotStopProcessException, IOException {
        process.destroy();
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final JavaProcessImpl other = (JavaProcessImpl) obj;
        if (process == null) {
            if (other.process != null) {
                return false;
            }
        } else if (!process.equals(other.process)) {
            return false;
        }
        return true;
    }

    /**
     * @return the List of arguments to the Java program
     */
    @Override
    public List<String> getArguments() {
        return new ArrayList<String>(arguments);
    }

    /**
     * @return the List which represents the command to execute this Java
     *         process
     */
    @Override
    public List<String> getCommand() {
        ArrayList<String> command = new ArrayList<String>();
        if (javaExecutable != null) {
            command.add(javaExecutable.getAbsolutePath());
        }
        if (vmOptions != null) {
            command.addAll(vmOptions);
        }
        command.addAll(getExecution());
        if (arguments != null) {
            command.addAll(arguments);
        }
        return command;
    }

    /**
     * @return the home directory of the process execution
     */
    @Override
    public File getDirectory() {
        return process.getDirectory();
    }

    @Override
    public Map<String, String> getEnvironment() {
        return process.getEnvironment();
    }

    @Override
    public Integer getExitValue() {
        return process.getExitValue();
    }

    /**
     * Answer the unique id of this java process
     */
    @Override
    public UUID getId() {
        return process.getId();
    }

    @Override
    public File getJarFile() {
        return jarFile;
    }

    @Override
    public String getJavaClass() {
        return javaClass;
    }

    /**
     * @return the File which points to the Java executable
     */
    @Override
    public File getJavaExecutable() {
        return javaExecutable;
    }

    /**
     * @throws ConnectException
     */
    @Override
    public JMXConnector getLocalJmxConnector(String connectorName)
            throws ConnectException, NoLocalJmxConnectionException {
        if (jmxc != null) {
            return jmxc;
        }

        if (!process.isActive()) {
            throw new ConnectException("Cannot establish local JMX connection as process is not active: " + this);
        }

        String address;
        try {
            VirtualMachine vm = VirtualMachine.attach("" + process.getPid());
            Properties props = vm.getSystemProperties();
            address = props.getProperty(connectorName);

            if (address == null) {
                throw new ConnectException(
                        "Unable to find address for remote JMX connection with name = " + connectorName);
            }
        } catch (IOException e) {
            ConnectException cex = new ConnectException("Cannot obtain local JMX connector address of: " + this);
            cex.initCause(e);
            throw cex;
        } catch (AttachNotSupportedException e) {
            throw new RuntimeException(e);
        }

        JMXServiceURL jmxUrl;
        try {
            jmxUrl = new JMXServiceURL(address);
        } catch (MalformedURLException e) {
            ConnectException cex = new ConnectException("Invalid local JMX URL for " + this + " : " + address);
            cex.initCause(e);
            throw cex;
        }

        try {
            jmxc = JMXConnectorFactory.connect(jmxUrl);
        } catch (java.rmi.ConnectException e) {
            if (e.getMessage().startsWith("Connection refused")) {
                throw new NoLocalJmxConnectionException("Local JMX connector address does not exist for: " + this);
            }
            ConnectException cex = new ConnectException("Underlying RMI communications exception");
            cex.initCause(e);
            throw cex;
        } catch (IOException e) {
            ConnectException cex = new ConnectException("Cannot establish local JMX connection to: " + this);
            cex.initCause(e);
            throw cex;
        }

        try {
            jmxc.connect();
        } catch (IOException e) {
            ConnectException cex = new ConnectException("Cannot establish local JMX connection to: " + this);
            cex.initCause(e);
            throw cex;
        }

        return jmxc;
    }

    @Override
    public MBeanServerConnection getLocalMBeanServerConnection(String connectionName)
            throws ConnectException, NoLocalJmxConnectionException {
        JMXConnector connector = getLocalJmxConnector(connectionName);

        try {
            return connector.getMBeanServerConnection();
        } catch (IOException e) {
            ConnectException cex = new ConnectException("Cannot establish local JMX connection to: " + this);
            cex.initCause(e);
            throw cex;
        }
    }

    @Override
    public MBeanServerConnection getLocalMBeanServerConnection(String connectionName, Subject delegationSubject)
            throws ConnectException, NoLocalJmxConnectionException {
        JMXConnector connector = getLocalJmxConnector(connectionName);

        try {
            return connector.getMBeanServerConnection(delegationSubject);
        } catch (IOException e) {
            ConnectException cex = new ConnectException("Cannot establish local JMX connection to: " + this);
            cex.initCause(e);
            throw cex;
        }
    }

    @Override
    public Integer getPid() {
        return process.getPid();
    }

    @Override
    public InputStream getStdErr() {
        return process.getStdErr();
    }

    @Override
    public String getStdErrTail(int numLines) throws IOException {
        return process.getStdErrTail(numLines);
    }

    @Override
    public OutputStream getStdIn() {
        return process.getStdIn();
    }

    @Override
    public InputStream getStdOut() {
        return process.getStdOut();
    }

    @Override
    public String getStdOutTail(int numLines) throws IOException {
        return process.getStdOutTail(numLines);
    }

    /**
     * @return the List of arguments to the Java virtual machine
     */
    @Override
    public List<String> getVmOptions() {
        return new ArrayList<String>(vmOptions);
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + (process == null ? 0 : process.hashCode());
        return result;
    }

    /**
     * @return true if the Java process is active
     */
    @Override
    public boolean isActive() {
        return process.isActive();
    }

    @Override
    public boolean isSameConfiguration(ManagedProcess otherProcess) {
        if (!(otherProcess instanceof JavaProcess)) {
            return false;
        }

        JavaProcess other = (JavaProcess) otherProcess;
        if (arguments == null) {
            if (other.getArguments() != null) {
                return false;
            }
        } else if (!arguments.equals(other.getArguments())) {
            return false;
        }
        if (jarFile == null) {
            if (other.getJarFile() != null) {
                return false;
            }
        } else if (!jarFile.equals(other.getJarFile())) {
            return false;
        }
        if (javaClass == null) {
            if (other.getJavaClass() != null) {
                return false;
            }
        } else if (!javaClass.equals(other.getJavaClass())) {
            return false;
        }
        if (javaExecutable == null) {
            if (other.getJavaExecutable() != null) {
                return false;
            }
        } else if (!javaExecutable.equals(other.getJavaExecutable())) {
            return false;
        }
        if (vmOptions == null) {
            if (other.getVmOptions() != null) {
                return false;
            }
        } else if (!vmOptions.equals(other.getVmOptions())) {
            return false;
        }
        return true;
    }

    /* (non-Javadoc)
     * @see com.hellblazer.process.ManagedProcess#restart()
     */
    @Override
    public void restart() throws IOException {
        process.restart();
    }

    /* (non-Javadoc)
     * @see com.hellblazer.process.ManagedProcess#restart(int)
     */
    @Override
    public void restart(int waitForSeconds) throws IOException {
        process.restart(waitForSeconds);
    }

    @Override
    public void setArguments(List<String> arguments) {
        if (arguments == null) {
            arguments = new ArrayList<String>();
        }
        this.arguments = new ArrayList<String>(arguments);
    }

    @Override
    public void setArguments(String[] arguments) {
        if (arguments == null) {
            arguments = new String[] {};
        }
        ArrayList<String> args = new ArrayList<String>();
        for (String argument : arguments) {
            args.add(argument);
        }
        setArguments(args);
    }

    @Override
    public void setCommand(List<String> commands) {
        throw new UnsupportedOperationException("Cannot set the command of the process directly");
    }

    @Override
    public void setCommand(String[] commands) {
        throw new UnsupportedOperationException("Cannot set the command of the process directly");
    }

    @Override
    public void setDirectory(File directory) {
        process.setDirectory(directory);
    }

    @Override
    public void setDirectory(String directory) {
        process.setDirectory(directory);
    }

    @Override
    public void setEnvironment(Map<String, String> environment) {
        process.setEnvironment(environment);
    }

    @Override
    public void setJarFile(File jarFile) {
        if (jarFile != null) {
            javaClass = null; // exclusive or
        }
        this.jarFile = jarFile;
    }

    @Override
    public void setJarFile(String jarFile) {
        if (jarFile == null) {
            this.jarFile = null;
            return;
        }
        setJarFile(new File(jarFile));
    }

    @Override
    public void setJavaClass(String javaClass) {
        if (javaClass != null) {
            jarFile = null; // exclusive or
        }
        this.javaClass = javaClass;
    }

    @Override
    public void setJavaExecutable(File javaExecutable) {
        this.javaExecutable = javaExecutable;
    }

    @Override
    public void setJavaExecutable(String javaExecutable) {
        if (javaExecutable == null) {
            this.javaExecutable = null;
            return;
        }
        setJavaExecutable(new File(javaExecutable));
    }

    @Override
    public void setVmOptions(List<String> vmOptions) {
        if (vmOptions == null) {
            vmOptions = new ArrayList<String>();
        }
        this.vmOptions = new ArrayList<String>(vmOptions);
    }

    @Override
    public void setVmOptions(String[] vmOptions) {
        if (vmOptions == null) {
            vmOptions = new String[] {};
        }
        ArrayList<String> options = new ArrayList<String>();
        for (String option : vmOptions) {
            options.add(option);
        }
        setVmOptions(options);
    }

    /**
     * Start the Java process
     * 
     * @throws IOException
     *             - if anything goes awry in starting up the process
     */
    @Override
    public synchronized void start() throws IOException {
        if (javaExecutable == null) {
            throw new IllegalStateException("Java executable must not be null");
        }
        if (vmOptions == null) {
            vmOptions = new ArrayList<String>();
        }
        if (arguments == null) {
            arguments = new ArrayList<String>();
        }
        process.setCommand(getCommand());
        process.start();
    }

    /**
     * Stop the execution of the Java process.
     * 
     * @throws CannotStopProcessException
     */
    @Override
    public synchronized void stop() throws CannotStopProcessException {
        process.stop();
    }

    @Override
    public synchronized void stop(int waitForSeconds) throws CannotStopProcessException {
        process.stop(waitForSeconds);
    }

    /* (non-Javadoc)
     * @see com.hellblazer.process.ManagedProcess#tailStdErr(org.apache.commons.io.input.TailerListener)
     */
    @Override
    public Tailer tailStdErr(TailerListener listener) {
        return process.tailStdErr(listener);
    }

    /* (non-Javadoc)
     * @see com.hellblazer.process.ManagedProcess#tailStdErr(org.apache.commons.io.input.TailerListener, long, boolean, boolean, int)
     */
    @Override
    public Tailer tailStdErr(TailerListener listener, long delayMillis, boolean end, boolean reOpen, int bufSize) {
        return process.tailStdErr(listener, delayMillis, end, reOpen, bufSize);
    }

    /* (non-Javadoc)
     * @see com.hellblazer.process.ManagedProcess#tailStdOut(org.apache.commons.io.input.TailerListener)
     */
    @Override
    public Tailer tailStdOut(TailerListener listener) {
        return process.tailStdOut(listener);
    }

    /* (non-Javadoc)
     * @see com.hellblazer.process.ManagedProcess#tailStdOut(org.apache.commons.io.input.TailerListener, long, boolean, boolean, int)
     */
    @Override
    public Tailer tailStdOut(TailerListener listener, long delayMillis, boolean end, boolean reOpen, int bufSize) {
        return tailStdOut(listener, delayMillis, end, reOpen, bufSize);
    }

    @Override
    public String toString() {
        StringBuffer buf = new StringBuffer();
        buf.append("JavaProcess");
        buf.append("{").append(getId()).append("} ");
        buf.append(" home dir: ");
        buf.append(getDirectory());
        buf.append(" pid: ");
        buf.append(getPid());
        return buf.toString();
    }

    @Override
    public int waitFor() throws InterruptedException {
        return process.waitFor();
    }

    /**
     * @return the List which represents the arguments to the VM invocation to
     *         run the Java program
     */
    protected List<String> getExecution() {
        assert !(jarFile != null && javaClass != null); // can't execute jar and java class simultaneously
        ArrayList<String> execution = new ArrayList<String>();
        if (jarFile != null) {
            execution.add("-jar");
            execution.add(jarFile.getAbsolutePath());
        } else {
            execution.add(javaClass);
        }
        return execution;
    }
}