/******************************************************************************* * Copyright (c) 2006 PalmSource, 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: * Ewa Matejska (PalmSource) - initial version * Martin Oberhuber (Wind River) - adapt to IHostOutput API (bug 161773, 158312) * Martin Oberhuber (Wind River) - moved from org.eclipse.rse.remotecdt (bug 161777) * Martin Oberhuber (Wind River) - renamed from HostShellAdapter (bug 161777) * Martin Oberhuber (Wind River) - improved Javadoc *******************************************************************************/ package org.eclipse.dltk.core.internal.rse; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; import org.eclipse.dltk.core.DLTKCore; import org.eclipse.dltk.core.environment.IExecutionLogger; import org.eclipse.rse.services.shells.HostShellOutputStream; import org.eclipse.rse.services.shells.IHostOutput; import org.eclipse.rse.services.shells.IHostShell; import org.eclipse.rse.services.shells.IHostShellChangeEvent; import org.eclipse.rse.services.shells.IHostShellOutputListener; import org.eclipse.rse.services.shells.IHostShellOutputReader; /** * This class represents a host shell process. It does not represent one process * running in the shell. This means that the output of multiple shell commands * will be returned until the shell exits. * * @author Ewa Matejska */ public class MyHostShellProcessAdapter extends Process implements IHostShellOutputListener { private final IExecutionLogger logger; private IHostShell hostShell; private PipedInputStream inputStream = null; private PipedInputStream errorStream = null; private HostShellOutputStream outputStream = null; private PipedOutputStream hostShellInput = null; private PipedOutputStream hostShellError = null; private String pattern1; private boolean done = false; /** * Constructor. * * @param hostShell * An instance of the IHostShell class. * @param logger * @param postfix * @throws java.io.IOException */ public MyHostShellProcessAdapter(IHostShell hostShell, String pattern1, IExecutionLogger logger) throws java.io.IOException { this.logger = logger; this.hostShell = hostShell; this.pattern1 = pattern1; hostShellInput = new PipedOutputStream(); hostShellError = new PipedOutputStream(); inputStream = new PipedInputStream(hostShellInput); errorStream = new PipedInputStream(hostShellError); outputStream = new HostShellOutputStream(hostShell); IHostShellOutputReader outputReader; outputReader = this.hostShell.getStandardOutputReader(); if (outputReader != null) { outputReader.addOutputListener(this); } outputReader = this.hostShell.getStandardErrorReader(); if (outputReader != null) { outputReader.addOutputListener(this); } } static final String CTRL_C = "\u0003"; //$NON-NLS-1$ /** * Exits the shell. * * @see java.lang.Process#destroy() */ public synchronized void destroy() { if (!done && hostShell.isActive()) { hostShell.writeToShell(CTRL_C); // let the shell time to terminate in standard way try { wait(1000); } catch (InterruptedException e) { // ignore } } hostShell.exit(); notifyAll(); closeStreams(); } private void closeStreams() { closeStreams(new Object[] { hostShellInput, hostShellError, inputStream, errorStream, outputStream }); } private void closeStreams(Object[] streams) { for (int i = 0; i < streams.length; ++i) { final Object stream = streams[i]; if (stream != null) { if (stream instanceof InputStream) { try { ((InputStream) stream).close(); } catch (IOException e) { if (DLTKCore.DEBUG) { e.printStackTrace(); } } } else if (stream instanceof OutputStream) { try { ((OutputStream) stream).close(); } catch (IOException e) { if (DLTKCore.DEBUG) { e.printStackTrace(); } } } else { DLTKRSEPlugin .log("closeStream(" + stream.getClass().getName() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ } } } } /** * There is no relevant exit value to return when the shell exits. This * always returns 0. */ public synchronized int exitValue() { if (!done && hostShell.isActive()) throw new IllegalThreadStateException(); // No way to tell what the exit value was. // TODO it would be possible to get the exit value // when the remote process is started like this: // sh -c' remotecmd ; echo" -->RSETAG<-- $?\"' // Then the output steram could be examined for -->RSETAG<-- to get the // exit value. return 0; } /** * Returns the error stream of the shell. * * @see java.lang.Process#getErrorStream() */ public InputStream getErrorStream() { return errorStream; } /** * Returns the input stream for the shell. * * @see java.lang.Process#getInputStream() */ public InputStream getInputStream() { return inputStream; } /** * Returns the output stream for the shell. * * @see java.lang.Process#getOutputStream() */ public OutputStream getOutputStream() { return outputStream; } /** * Waits for the shell to exit. * * @see java.lang.Process#waitFor() */ public synchronized int waitFor() throws InterruptedException { while (!done && hostShell.isActive()) { try { wait(1000); } catch (InterruptedException e) { // ignore because we're polling to see if shell is still active. } } try { // Wait a second to try to get some more output from the target // shell before closing. wait(1000); // Allow for the data from the stream to be read if it's available if (inputStream.available() != 0 || errorStream.available() != 0) throw new InterruptedException(); hostShell.exit(); closeStreams(); } catch (IOException e) { // Ignore } return 0; } private synchronized void endOfOutput() { done = true; notifyAll(); } /** * Process an RSE Shell event, by writing the lines of text contained in the * event into the adapter's streams. * * @see org.eclipse.rse.services.shells.IHostShellOutputListener# * shellOutputChanged * (org.eclipse.rse.services.shells.IHostShellChangeEvent) */ private int prefixCounter = 0; public void shellOutputChanged(IHostShellChangeEvent event) { IHostOutput[] input = event.getLines(); OutputStream outputStream = event.isError() ? hostShellError : hostShellInput; try { for (int i = 0; i < input.length; i++) { String line = input[i].getString(); if (logger != null) { logger.logLine(line); } if (line == null) { continue; } if (!event.isError()) { String trimLine = line.trim(); if (trimLine.endsWith(this.pattern1)) { if (prefixCounter == 1 && !trimLine.equals(pattern1)) { // We need to output part of line int pos = line.indexOf(pattern1); outputStream.write(line.substring(0, pos) .getBytes()); outputStream.write('\n'); outputStream.flush(); } prefixCounter++; if (prefixCounter == 2) { endOfOutput(); return; } continue; } } if (prefixCounter == 1) { outputStream.write(line.getBytes()); outputStream.write('\n'); outputStream.flush(); } } } catch (IOException e) { // Ignore } } }