org.eclipse.ecf.python.AbstractPythonLauncher.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.ecf.python.AbstractPythonLauncher.java

Source

/*******************************************************************************
 * Copyright (c) 2018 Composent, Inc. and others. All rights reserved. This
 * program and the accompanying materials are made available under the terms of
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
 * available at http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors: Composent, Inc. - initial API and implementation
 ******************************************************************************/
package org.eclipse.ecf.python;

import java.io.File;
import java.io.OutputStream;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecuteResultHandler;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.ExecuteException;
import org.apache.commons.exec.ExecuteResultHandler;
import org.apache.commons.exec.Executor;
import org.apache.commons.exec.LogOutputStream;
import org.apache.commons.exec.ProcessDestroyer;
import org.apache.commons.exec.PumpStreamHandler;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.service.component.ComponentConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class AbstractPythonLauncher implements PythonLauncher {

    public static final String PYTHON_LAUNCH_COMMAND_OPTION = "-c";

    protected static final Logger logger = LoggerFactory.getLogger(AbstractPythonLauncher.class);
    protected Object launchLock = new Object();
    protected boolean enabled = Boolean
            .valueOf(System.getProperty("org.eclipse.ecf.python.PythonLauncher.enabled", "true")).booleanValue();
    protected String pythonExec = System.getProperty("org.eclipse.ecf.python.PythonLauncher.pythonExec", "python");
    protected File pythonWorkingDirectory = Paths
            .get(System.getProperty("org.eclipse.ecf.python.PythonLauncher.workingDirectory", ".")).toFile();
    protected BundleContext context;
    protected Executor executor;
    protected boolean shuttingDown;

    protected Integer javaPort;
    protected Integer pythonPort;

    protected List<ServiceReference<PythonLaunchCommandProvider>> launchCommandProviders = Collections
            .synchronizedList(new ArrayList<ServiceReference<PythonLaunchCommandProvider>>());

    protected void bindLaunchCommandProvider(ServiceReference<PythonLaunchCommandProvider> provider) {
        addPythonLaunchCommandProvider(provider);
    }

    protected void unbindPythonLaunchCommandProvider(ServiceReference<PythonLaunchCommandProvider> provider) {
        removePythonLaunchCommandProvider(provider);
    }

    protected void addPythonLaunchCommandProvider(ServiceReference<PythonLaunchCommandProvider> provider) {
        launchCommandProviders.add(provider);
    }

    protected void removePythonLaunchCommandProvider(ServiceReference<PythonLaunchCommandProvider> provider) {
        launchCommandProviders.remove(provider);
    }

    protected void activate(BundleContext context) throws Exception {
        this.context = context;
    }

    protected void deactivate(BundleContext context) {
        this.context = null;
    }

    protected BundleContext getContext() {
        return this.context;
    }

    protected class PythonProcessDestroyer implements ProcessDestroyer {

        private List<Process> processes = Collections.synchronizedList(new ArrayList<Process>());

        @Override
        public boolean add(Process arg0) {
            return processes.add(arg0);
        }

        @Override
        public boolean remove(Process arg0) {
            return processes.remove(arg0);
        }

        @Override
        public int size() {
            return processes.size();
        }

        public void destroyAll() {
            for (Process p : processes)
                try {
                    p.destroy();
                } catch (Exception e) {
                    logger.error("Exception destroying process=" + p);
                }
        }
    }

    protected Executor createExecutor() {
        return new DefaultExecutor();
    }

    protected String createPythonLaunchCommand() {
        BundleContext context = getContext();
        if (context == null)
            return null;
        List<ServiceReference<PythonLaunchCommandProvider>> lcp = null;
        synchronized (this.launchCommandProviders) {
            lcp = new ArrayList<ServiceReference<PythonLaunchCommandProvider>>(this.launchCommandProviders);
        }
        // Sort by ranking
        Collections.sort(lcp, new Comparator<ServiceReference<PythonLaunchCommandProvider>>() {
            @Override
            public int compare(ServiceReference<PythonLaunchCommandProvider> o1,
                    ServiceReference<PythonLaunchCommandProvider> o2) {
                Integer o1ranking = (Integer) o1.getProperty(Constants.SERVICE_RANKING);
                Integer o2ranking = (Integer) o2.getProperty(Constants.SERVICE_RANKING);
                return (o2ranking == null ? 0 : o2ranking.intValue())
                        - (o1ranking == null ? 0 : o1ranking.intValue());
            }
        });
        StringBuffer buf = new StringBuffer("");
        for (Iterator<ServiceReference<PythonLaunchCommandProvider>> i = lcp.iterator(); i.hasNext();) {
            ServiceReference<PythonLaunchCommandProvider> ref = i.next();
            String componentName = (String) ref.getProperty(ComponentConstants.COMPONENT_NAME);
            if (componentName != null)
                componentName = "PythonLaunchCommandProvider component name=" + componentName + "\n";
            else
                componentName = "PythonLaunchCommandProvider service id=" + ref.getProperty(Constants.SERVICE_ID)
                        + "\n";

            PythonLaunchCommandProvider plcp = context.getService(ref);
            buf.append("## Start of " + componentName);
            buf.append(plcp.getLaunchCommand());
            buf.append("## End of " + componentName);
            context.ungetService(ref);
        }
        return buf.toString();
    }

    @Override
    public void launch(String[] args, OutputStream output) throws Exception {
        synchronized (this.launchLock) {
            if (isLaunched())
                throw new IllegalStateException("Already started");

            this.shuttingDown = false;
            if (enabled) {
                String pythonLaunchCommand = createPythonLaunchCommand();
                if (pythonLaunchCommand == null)
                    throw new NullPointerException("pythonLaunchCommand must not be null");

                logger.debug("pythonLaunchCommand=" + pythonLaunchCommand);

                this.executor = createExecutor();
                if (this.pythonWorkingDirectory != null)
                    this.executor.setWorkingDirectory(pythonWorkingDirectory);

                if (output == null) {
                    output = new LogOutputStream() {
                        @Override
                        protected void processLine(String line, int level) {
                            logger.debug("PYTHON: " + line);
                        }
                    };
                }
                executor.setStreamHandler(new PumpStreamHandler(output));

                this.executor.setProcessDestroyer(new PythonProcessDestroyer());

                ExecuteResultHandler executeHandler = new DefaultExecuteResultHandler() {
                    @Override
                    public void onProcessComplete(int exitValue) {
                        logger.debug("PYTHON EXIT=" + exitValue);
                    }

                    @Override
                    public void onProcessFailed(ExecuteException e) {
                        if (!shuttingDown)
                            logger.debug("PYTHON EXCEPTION", e);
                    }
                };

                CommandLine commandLine = new CommandLine(pythonExec).addArgument(PYTHON_LAUNCH_COMMAND_OPTION);
                commandLine.addArgument(pythonLaunchCommand, true);

                List<String> argsList = (args == null) ? Collections.emptyList() : Arrays.asList(args);

                if (this.javaPort != null && !argsList.contains(JAVA_PORT_OPTION)) {
                    commandLine.addArgument(JAVA_PORT_OPTION);
                    commandLine.addArgument(String.valueOf(this.javaPort));
                }

                if (this.pythonPort != null && !argsList.contains(PYTHON_PORT_OPTION)) {
                    commandLine.addArgument(PYTHON_PORT_OPTION);
                    commandLine.addArgument(String.valueOf(this.pythonPort));
                }

                if (args != null)
                    commandLine.addArguments(args);
                logger.debug("PythonLauncher.launch: " + commandLine);
                try {
                    executor.execute(commandLine, executeHandler);
                } catch (Exception e) {
                    this.executor = null;
                    throw e;
                }
            } else
                logger.debug("PythonLauncher DISABLED.   Python process must be started manually");
        }
    }

    @Override
    public void halt() {
        synchronized (this.launchLock) {
            if (!isLaunched())
                return;
            if (this.executor != null) {
                this.shuttingDown = true;
                PythonProcessDestroyer destroyer = (PythonProcessDestroyer) this.executor.getProcessDestroyer();
                if (destroyer != null)
                    destroyer.destroyAll();
                this.executor = null;
            }
        }
    }

    @Override
    public boolean isLaunched() {
        synchronized (this.launchLock) {
            return this.executor != null;
        }
    }

}