/******************************************************************************* * Copyright (c) 2014, 2015 Wind River Systems, Inc. * 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: * Markus Schorn - initial API and implementation *******************************************************************************/ package org.eclipse.tcf.te.tcf.remote.core; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.util.EventObject; import org.eclipse.remote.core.IRemoteConnection; import org.eclipse.remote.core.IRemoteProcess; import org.eclipse.remote.core.IRemoteProcessBuilder; import org.eclipse.remote.core.IRemoteProcessControlService; import org.eclipse.tcf.services.IProcesses.ProcessContext; import org.eclipse.tcf.te.runtime.events.EventManager; import org.eclipse.tcf.te.runtime.interfaces.events.IEventListener; import org.eclipse.tcf.te.tcf.processes.core.launcher.ProcessLauncher; import org.eclipse.tcf.te.tcf.processes.core.launcher.ProcessStateChangeEvent; public class TCFProcess implements IRemoteProcess, IRemoteProcessControlService, IEventListener { private InputStream fStdout; private InputStream fStderr; private OutputStream fStdin; private PipedOutputStream fCombinedOutput; private int fReadersDone; private final ProcessLauncher fLauncher; private int fExitValue; private boolean fCompleted; private IRemoteProcessBuilder fBuilder; private class StreamForwarder implements Runnable { private final static int BUF_SIZE = 8192; private final InputStream fInput; private final OutputStream fOutput; public StreamForwarder(InputStream input, OutputStream output) { fInput = input; fOutput = output; } @Override public void run() { int len; byte b[] = new byte[BUF_SIZE]; try { while ((len = fInput.read(b)) > 0) { fOutput.write(b, 0, len); } } catch (IOException e) { } onProcReaderDone(); } } private static class NullInputStream extends InputStream { public NullInputStream() { } @Override public int read() throws IOException { return -1; } @Override public int available() { return 0; } } public TCFProcess(TCFProcessBuilder builder, ProcessLauncher launcher) { fBuilder = builder; fLauncher = launcher; EventManager.getInstance().addEventListener(this, ProcessStateChangeEvent.class); } @Override public IRemoteProcessBuilder getProcessBuilder() { return fBuilder; } @Override public IRemoteConnection getRemoteConnection() { return fBuilder.getRemoteConnection(); } @Override public IRemoteProcess getRemoteProcess() { return this; } @Override public <T extends Service> T getService(Class<T> service) { if (service.isAssignableFrom(TCFProcess.class)) return service.cast(this); return null; } @Override public <T extends Service> boolean hasService(Class<T> service) { return service.isAssignableFrom(TCFProcess.class); } public void connectStreams(TCFProcessStreams streams, boolean redirectStderr) throws IOException { if (redirectStderr) { fCombinedOutput = new PipedOutputStream(); fStdout = new PipedInputStream(fCombinedOutput); fStderr = null; new Thread(new StreamForwarder(streams.getStdout(), fCombinedOutput)).start(); new Thread(new StreamForwarder(streams.getStderr(), fCombinedOutput)).start(); } else { fStdout = streams.getStdout(); fStderr = streams.getStderr(); } fStdin = streams.getStdin(); } protected void onProcReaderDone() { synchronized (this) { if (++fReadersDone == 2) { try { fCombinedOutput.close(); } catch (IOException e) { } } } } @Override public void destroy() { synchronized(this) { if (fCompleted) return; } fLauncher.cancel(); fLauncher.terminate(); synchronized (this) { if (!fCompleted) { fExitValue = -1; fCompleted = true; notifyAll(); } } EventManager.getInstance().removeEventListener(this); } @Override public int exitValue() { return fExitValue; } @Override public InputStream getErrorStream() { return fStderr != null ? fStderr : new NullInputStream(); } @Override public InputStream getInputStream() { return fStdout != null ? fStdout : new NullInputStream(); } @Override public OutputStream getOutputStream() { return fStdin; } @Override public int waitFor() throws InterruptedException { synchronized (this) { while (!isCompleted()) { wait(); } } return exitValue(); } @Override public boolean isCompleted() { return fCompleted; } @Override public void eventFired(EventObject event) { if (event instanceof ProcessStateChangeEvent) { ProcessStateChangeEvent pscEvent = (ProcessStateChangeEvent) event; if (pscEvent.getEventId().equals(ProcessStateChangeEvent.EVENT_PROCESS_TERMINATED)) { Object source = pscEvent.getSource(); if ((source instanceof ProcessContext)) { ProcessContext context = (ProcessContext) fLauncher.getAdapter(ProcessContext.class); if (context != null && ((ProcessContext) source).getID().equals(context.getID())) { synchronized (this) { fExitValue = pscEvent.getExitCode(); fCompleted = true; notifyAll(); } } } } } } }