/** * Copyright (c) Red Hat, Inc., contributors and others 2013 - 2014. All rights reserved * * Licensed under the Eclipse Public License version 1.0, available at * http://www.eclipse.org/legal/epl-v10.html */ package org.jboss.tools.forge.core.internal.runtime; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.IDebugEventSetListener; import org.eclipse.debug.core.IStreamListener; import org.eclipse.debug.core.model.IProcess; import org.eclipse.debug.core.model.IStreamMonitor; import org.eclipse.debug.core.model.IStreamsProxy; import org.jboss.tools.forge.core.internal.ForgeCorePlugin; import org.jboss.tools.forge.core.internal.process.ForgeLaunchHelper; import org.jboss.tools.forge.core.internal.process.ForgeRuntimeProcess; import org.jboss.tools.forge.core.io.ForgeHiddenOutputFilter; import org.jboss.tools.forge.core.io.ForgeOutputListener; import org.jboss.tools.forge.core.runtime.ForgeRuntime; import org.jboss.tools.forge.core.runtime.ForgeRuntimeState; public abstract class ForgeAbstractRuntime implements ForgeRuntime { private IProcess process = null; private ForgeRuntimeState state = ForgeRuntimeState.STOPPED; private String version = null; private final TerminateListener terminateListener = new TerminateListener(); private MasterStreamListener masterStreamListener = new MasterStreamListener(); private CommandResultListener commandResultListener = new CommandResultListener(); private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this); private List<ForgeOutputListener> outputListeners = new ArrayList<>(); public IProcess getProcess() { return process; } @Override public ForgeRuntimeState getState() { return state; } @Override public String getVersion() { if (version == null) { version = initializeVersion(); } return version; } private String initializeVersion() { String result = "unknown version"; String location = getLocation(); if (location == null) return result; location += "/modules/org/jboss/forge/shell/api/main"; File file = new File(location); if (!file.exists()) return result; String[] candidates = file.list(); for (String candidate : candidates) { if (candidate.startsWith("forge-shell-api-")) { int end = candidate.indexOf(".jar"); if (end != -1) { result = candidate.substring("forge-shell-api-".length(), end); } } } return result; } @Override public void start(IProgressMonitor progressMonitor) { ForgeCorePlugin.getDefault().sendStartEvent(this); errorMessage = null; IStreamListener startupListener = null; IStreamListener errorListener = null; if (progressMonitor == null) { progressMonitor = new NullProgressMonitor(); } try { progressMonitor.beginTask("Starting Forge " + getVersion(), IProgressMonitor.UNKNOWN); startupListener = new StartupListener(); process = ForgeLaunchHelper.launch(getName(), getLocation()); if (process != null) { setNewState(ForgeRuntimeState.STARTING); DebugPlugin.getDefault().addDebugEventListener(terminateListener); IStreamsProxy streamsProxy = getStreamsProxy(); if (streamsProxy != null) { IStreamMonitor outputStreamMonitor = streamsProxy.getOutputStreamMonitor(); if (outputStreamMonitor != null) { outputStreamMonitor.addListener(startupListener); outputStreamMonitor.addListener(masterStreamListener); outputStreamMonitor.addListener(commandResultListener); } IStreamMonitor errorStreamMonitor = streamsProxy.getErrorStreamMonitor(); if (errorStreamMonitor != null) { errorStreamMonitor.addListener(masterStreamListener); errorListener = new ErrorListener(errorStreamMonitor); } } } progressMonitor.worked(1); while (ForgeRuntimeState.STARTING.equals(state)) { if (process.isTerminated()) { setNewState(ForgeRuntimeState.STOPPED); progressMonitor.done(); return; } if (progressMonitor.isCanceled()) { terminate(); } else { Thread.sleep(1000); progressMonitor.worked(1); } } } catch (InterruptedException e) { if (progressMonitor.isCanceled()) { terminate(); } } finally { if (process != null) { if (!process.isTerminated()) { ForgeCorePlugin.addForgeProcess(process); } IStreamsProxy streamsProxy = getStreamsProxy(); if (streamsProxy != null) { IStreamMonitor outputStreamMonitor = streamsProxy.getOutputStreamMonitor(); if (outputStreamMonitor != null) { outputStreamMonitor.removeListener(startupListener); } IStreamMonitor errorStreamMonitor = streamsProxy.getErrorStreamMonitor(); if (errorStreamMonitor != null && errorListener != null) { errorStreamMonitor.removeListener(errorListener); } } } progressMonitor.done(); } } private boolean commandResultAvailable = false; private String commandResult = null; private Object mutex = new Object(); private String errorMessage = null; @Override public String getErrorMessage() { return errorMessage; } @Override public String sendCommand(String str) { // System.out.println("sendCommand(" + str + ")"); String result = null; if (process != null && !process.isTerminated()) { IStreamsProxy streamsProxy = getStreamsProxy(); if (streamsProxy != null) { IStreamMonitor errorStreamMonitor = streamsProxy.getErrorStreamMonitor(); errorStreamMonitor.removeListener(masterStreamListener); IStreamMonitor streamMonitor = streamsProxy.getOutputStreamMonitor(); if (streamMonitor != null) { synchronized (mutex) { try { streamsProxy.write(Character.toString((char) 31) + str + '\n'); } catch (IOException e) { ForgeCorePlugin.log(e); } while (!commandResultAvailable) { try { mutex.wait(); } catch (InterruptedException e) { } } } result = commandResult; commandResult = null; commandResultAvailable = false; } errorStreamMonitor.addListener(masterStreamListener); } } // System.out.println("ForgeAbstractRuntime.sendCommand result: " + // result); return result; } @Override public void sendInput(String str) { if (process != null && !process.isTerminated()) { IStreamsProxy streamProxy = getStreamsProxy(); if (streamProxy != null) { try { streamProxy.write(str); } catch (IOException e) { ForgeCorePlugin.log(e); } } } } @Override public void stop(IProgressMonitor progressMonitor) { if (progressMonitor == null) { progressMonitor = new NullProgressMonitor(); } try { progressMonitor.beginTask("Stopping Forge", 1); terminate(); } finally { progressMonitor.done(); } } private void terminate() { try { if (process != null) { IStreamsProxy streamsProxy = getStreamsProxy(); if (streamsProxy != null) { IStreamMonitor outputStreamMonitor = streamsProxy.getOutputStreamMonitor(); if (outputStreamMonitor != null) { outputStreamMonitor.removeListener(masterStreamListener); } IStreamMonitor errorStreamMonitor = streamsProxy.getErrorStreamMonitor(); if (errorStreamMonitor != null) { errorStreamMonitor.removeListener(masterStreamListener); } } process.terminate(); ForgeCorePlugin.removeForgeProcess(process); } } catch (DebugException e) { ForgeCorePlugin.log(e); } } private void setNewState(ForgeRuntimeState newState) { ForgeRuntimeState oldState = state; state = newState; propertyChangeSupport.firePropertyChange(PROPERTY_STATE, oldState, state); } private IStreamsProxy getStreamsProxy() { if (process instanceof ForgeRuntimeProcess) { return ((ForgeRuntimeProcess) process).getForgeStreamsProxy(); } return process.getStreamsProxy(); } @Override public void addPropertyChangeListener(PropertyChangeListener propertyChangeListener) { propertyChangeSupport.addPropertyChangeListener(propertyChangeListener); } @Override public void removePropertyChangeListener(PropertyChangeListener propertyChangeListener) { propertyChangeSupport.removePropertyChangeListener(propertyChangeListener); } @Override public void addOutputListener(ForgeOutputListener listener) { outputListeners.add(listener); } @Override public void removeOutputListener(ForgeOutputListener listener) { outputListeners.remove(listener); } private class StartupListener implements IStreamListener { @Override public void streamAppended(String text, IStreamMonitor monitor) { getStreamsProxy().getOutputStreamMonitor().removeListener(this); setNewState(ForgeRuntimeState.RUNNING); } } private class ErrorListener implements IStreamListener { private IStreamMonitor fMonitor; public ErrorListener(IStreamMonitor monitor) { fMonitor = monitor; monitor.addListener(this); // make sure that output is processed if forge process dies quickly streamAppended(null, fMonitor); } @Override public void streamAppended(String text, IStreamMonitor monitor) { if (text == null) return; errorMessage = monitor.getContents(); ForgeCorePlugin.logErrorMessage(errorMessage); } } private class MasterStreamListener implements IStreamListener { @Override public void streamAppended(String text, IStreamMonitor monitor) { for (ForgeOutputListener listener : outputListeners) { listener.outputAvailable(text); } } } private class CommandResultListener extends ForgeHiddenOutputFilter implements IStreamListener { @Override public void streamAppended(String text, IStreamMonitor monitor) { // System.out.println("CommandResultListener.streamAppended(" + text // + ")"); outputAvailable(text); } @Override public void handleFilteredString(String str) { // System.out.println("CommandResultListener.handleFilteredString(" // + str + ")"); if (str.startsWith("RESULT: ")) { commandResult = str.substring(8); commandResultAvailable = true; synchronized (mutex) { mutex.notifyAll(); } } } } private class TerminateListener implements IDebugEventSetListener { @Override public void handleDebugEvents(DebugEvent[] events) { for (int i = 0; i < events.length; i++) { DebugEvent event = events[i]; if (event.getSource().equals(process)) { if (event.getKind() == DebugEvent.TERMINATE) { DebugPlugin.getDefault().asyncExec(new Runnable() { @Override public void run() { setNewState(ForgeRuntimeState.STOPPED); ForgeCorePlugin.removeForgeProcess(process); process = null; DebugPlugin.getDefault().removeDebugEventListener(terminateListener); } }); } } } } } }