From 5bb00b500976002872921f28f32debebfe977d15 Mon Sep 17 00:00:00 2001 From: Martin Oberhuber < martin.oberhuber@windriver.com> Date: Fri, 11 Apr 2008 13:36:45 +0000 Subject: [PATCH] [170910][api] Add API for Stream-based shells and terminals in RSE --- .../META-INF/MANIFEST.MF | 1 + .../terminals/AbstractTerminalShell.java | 77 ++++++++ .../terminals/BaseShellDecorator.java | 85 +++++++++ .../services/terminals/IBaseShell.java | 167 ++++++++++++++++++ .../services/terminals/ITerminalService.java | 92 ++++++++++ .../services/terminals/ITerminalShell.java | 101 +++++++++++ .../services/terminals/ProcessBaseShell.java | 148 ++++++++++++++++ .../terminals/TerminalShellDecorator.java | 59 +++++++ 8 files changed, 730 insertions(+) create mode 100644 rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/AbstractTerminalShell.java create mode 100644 rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/BaseShellDecorator.java create mode 100644 rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/IBaseShell.java create mode 100644 rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/ITerminalService.java create mode 100644 rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/ITerminalShell.java create mode 100644 rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/ProcessBaseShell.java create mode 100644 rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/TerminalShellDecorator.java diff --git a/rse/plugins/org.eclipse.rse.services/META-INF/MANIFEST.MF b/rse/plugins/org.eclipse.rse.services/META-INF/MANIFEST.MF index fa7c7d9ac07..4892387c897 100644 --- a/rse/plugins/org.eclipse.rse.services/META-INF/MANIFEST.MF +++ b/rse/plugins/org.eclipse.rse.services/META-INF/MANIFEST.MF @@ -13,6 +13,7 @@ Export-Package: org.eclipse.rse.internal.services;x-internal:=true, org.eclipse.rse.internal.services.clientserver.archiveutils;x-internal:=true, org.eclipse.rse.internal.services.clientserver.java;x-internal:=true, org.eclipse.rse.internal.services.shells;x-internal:=true, + org.eclipse.rse.internal.services.terminals;x-internal:=true, org.eclipse.rse.services, org.eclipse.rse.services.clientserver, org.eclipse.rse.services.clientserver.archiveutils, diff --git a/rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/AbstractTerminalShell.java b/rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/AbstractTerminalShell.java new file mode 100644 index 00000000000..44527624bdc --- /dev/null +++ b/rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/AbstractTerminalShell.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems, 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: + * Martin Oberhuber (Wind River) - initial API and implementation + *******************************************************************************/ + +package org.eclipse.rse.internal.services.terminals; + +import java.io.InputStream; + +import org.eclipse.core.runtime.PlatformObject; + +/** + * Abstract base class for clients to create an ITerminalShell instance. + * + * This abstract base class provides valid default implementations for all + * {@link ITerminalShell} methods where possible. Clients should extend this + * base class rather than implementing ITerminalShell directly, in order to + * remain compatible when the ITerminalShell interface is evolved in the future. + * + *
+ * EXPERIMENTAL. This class or interface has been added as + * part of a work in progress. There is no guarantee that this API will work or + * that it will remain the same. Please do not use this API without consulting + * with the Target Management + * team. + *
+ * + * @since org.eclipse.rse.services 3.0 + */ +public abstract class AbstractTerminalShell extends PlatformObject implements ITerminalShell { + + public String getDefaultEncoding() { + return null; + } + + public String getPtyType() { + return null; + } + + public boolean isLocalEcho() { + return false; + } + + public void setTerminalSize(int newWidth, int newHeight) { + // do nothing + } + + public InputStream getErrorStream() { + return null; + } + + public int exitValue() { + // exit values are not supported by default, but we need to observe the + // API by throwing IllegalThreadStateException + if (isActive()) + throw new IllegalThreadStateException(); + return 0; + } + + public boolean waitFor(long timeout) throws InterruptedException { + boolean active = isActive(); + if (active) { + synchronized (this) { + wait(timeout); + } + active = isActive(); + } + return active; + } + +} diff --git a/rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/BaseShellDecorator.java b/rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/BaseShellDecorator.java new file mode 100644 index 00000000000..3d898d20212 --- /dev/null +++ b/rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/BaseShellDecorator.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems, 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: + * Martin Oberhuber (Wind River) - initial API and implementation + *******************************************************************************/ + +package org.eclipse.rse.internal.services.terminals; + +import java.io.InputStream; +import java.io.OutputStream; + +import org.eclipse.core.runtime.PlatformObject; + +/** + * Abstract base class for clients to decorate an IBaseShell instance they have + * with additional functionality. + * + * Typically, such additional functionality can be provided either by additional + * knowledge about the underlying system or process, or by analyzing the input + * and output streams for some well-known data that gives such additional + * knowledge. + * + *+ * EXPERIMENTAL. This class or interface has been added as + * part of a work in progress. There is no guarantee that this API will work or + * that it will remain the same. Please do not use this API without consulting + * with the Target Management + * team. + *
+ * + * @since org.eclipse.rse.services 3.0 + */ +public abstract class BaseShellDecorator extends PlatformObject implements IBaseShell { + + protected final IBaseShell fDelegate; + + public BaseShellDecorator(IBaseShell delegate) { + fDelegate = delegate; + } + + public void exit() { + fDelegate.exit(); + } + + public int exitValue() { + return fDelegate.exitValue(); + } + + public InputStream getErrorStream() { + return fDelegate.getErrorStream(); + } + + public InputStream getInputStream() { + return fDelegate.getInputStream(); + } + + public OutputStream getOutputStream() { + return fDelegate.getOutputStream(); + } + + public boolean isActive() { + return fDelegate.isActive(); + } + + public boolean waitFor(long timeout) throws InterruptedException { + return fDelegate.waitFor(timeout); + } + + public Object getAdapter(Class adapterType) { + // TODO do we want to delegate here or have our own adapter??? + // I think we're most flexible first letting adapt to ourselves, + // and only if not successful then ask the delegate to adapt. + Object adapter = super.getAdapter(adapterType); + if (adapter == null) { + adapter = fDelegate.getAdapter(adapterType); + } + return adapter; + } + +} diff --git a/rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/IBaseShell.java b/rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/IBaseShell.java new file mode 100644 index 00000000000..de65abb1bee --- /dev/null +++ b/rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/IBaseShell.java @@ -0,0 +1,167 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems, 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: + * Martin Oberhuber (Wind River) - initial API and implementation + *******************************************************************************/ + +package org.eclipse.rse.internal.services.terminals; + +import java.io.InputStream; +import java.io.OutputStream; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.PlatformObject; + +/** + * A basic shell, representing the connection to some process that may be + * running local or remote. Clients may implement this interface. + * + * Clients implementing this interface are encouraged to extend + * {@link PlatformObject} for providing the {@link #getAdapter(Class)} + * functionality. + * + * A simple implementation of IBaseShell is the {@link ProcessBaseShell}, which + * wraps a Java {@link java.lang.Process} object in the IBaseShell interface to + * provide more convenient access to it through the {{@link #isActive()} and {{@link #waitFor(long)} + * methods, as well as making it adaptable. + * + * The resulting IBaseShell can be decorated by clients with additional + * functionality easily by instantiating their subclassed variant of + * {@link BaseShellDecorator}. + * + *+ * EXPERIMENTAL. This class or interface has been added as + * part of a work in progress. There is no guarantee that this API will work or + * that it will remain the same. Please do not use this API without consulting + * with the Target Management + * team. + *
+ * + * @see java.lang.Process + * @see ProcessBaseShell + * @see BaseShellDecorator + * @since org.eclipse.rse.services 3.0 + */ +public interface IBaseShell extends IAdaptable { + + /** + * Get a local-to-remote OutputStream connected to the standard input of the + * underlying Process. + * + * Clients must not close the obtained OutputStream themselves, since the + * behavior that this may have on the underlying shell or process is + * undefined. Use {#exit()} instead to terminate the shell if that is + * desired, it will close all relevant Streams. + * + * @return an OutputStream for talking to the underlying process. + */ + public OutputStream getOutputStream(); + + /** + * Get a remote-to-local InputStream connected to the standard output of the + * underlying Process. + * + * Clients must not close the obtained InputStream themselves, since the + * behavior that this may have on the underlying shell or process is + * undefined. Use {#exit()} instead to terminate the shell if that is + * desired, it will close all relevant Streams. + * + * @return an InputStream for reading from the underlying process. + */ + public InputStream getInputStream(); + + /** + * Get a remote-to-local InputStream connected to the standard error output + * of the underlying Process. + * + * Implementations may returnnull
if they do not support
+ * separate Streams for output and error.
+ *
+ * Clients must not close the obtained InputStream themselves, since the
+ * behavior that this may have on the underlying shell or process is
+ * undefined. Use {#exit()} instead to terminate the shell if that is
+ * desired, it will close all relevant Streams.
+ *
+ * @return an InputStream for reading error output from the underlying
+ * process, or null
if separate output and error
+ * streams are not supported. Error output will be merged with the
+ * Stream obtained from {@link #getInputStream()} in that case.
+ */
+ public InputStream getErrorStream();
+
+ /**
+ * Test whether this connection is active.
+ *
+ * @return true
if the connection is active, i.e. the Process
+ * underlying this connection is running, and the Streams connected
+ * to it are not closed.
+ * @see #exitValue()
+ */
+ public boolean isActive();
+
+ /**
+ * Exit this shell.
+ *
+ * Implementations are encouraged to try terminating the underlying process
+ * in a clean way, if they can. Depending on the implementation, this may be
+ * possible or not. What's guaranteed to happen is that the Streams
+ * connected with the process are closed so the shell will not be active any
+ * more.
+ *
+ * Execution of this method may run asynchronously in the sense that the
+ * method performs everything to initiate terminating the shell but then
+ * returns immediately. Clients may use {@link #waitFor(long)} after calling
+ * this method to know when the shell is really terminated. At any rate, the
+ * exit() method returns quickly and guarantees that the shell will be
+ * terminated as soon as possible, after any required (and possible) cleanup
+ * is finished.
+ *
+ * @see java.lang.Process#destroy()
+ */
+ public void exit();
+
+ /**
+ * Return the exit value of the Process connected by this shell.
+ *
+ * Depending on the underlying implementation, this call may not be
+ * supported. Implementations which do not support this must throw an
+ * IllegalThreadStateException when the shell is still active, or return 0
+ * the shell has terminated.
+ *
+ * @return the exit value of the Process connected by this shell, provided
+ * that it has already terminated. By convention, the exit value 0
+ * indicates successful completion or the fact that transmission of
+ * exit values is not supported by an implementation.
+ * @exception IllegalThreadStateException when the shell is still active.
+ * @see java.lang.Process#exitValue()
+ */
+ public int exitValue();
+
+ /**
+ * Block the calling Thread until this shell is no longer active, or the
+ * specified timeout has elapsed. If the underlying shell has already
+ * terminated, this method returns immediately.
+ *
+ * When this method returns false
, the shell is no longer
+ * active, so an {@link #exitValue()} may be obtained.
+ *
+ * @param timeout the maximum time (in milliseconds) to wait.
+ * Implementations may return sooner even if the underlying
+ * Process has not yet terminated, so clients always need to keep
+ * track of time themselves and need to check the return value. A
+ * timeout value of zero causes this method to not limit the wait
+ * time. Negative wait time has undefined behavior.
+ * @return true
if the Shell is still active after waiting
+ * (e.g. because the timeout has elapsed); false
if
+ * the shell is no longer active.
+ * @throws InterruptedException if the waiting Thread has been interrupted,
+ * e.g. because the main application is shutting down.
+ * @see #isActive()
+ */
+ public boolean waitFor(long timeout) throws InterruptedException;
+}
diff --git a/rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/ITerminalService.java b/rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/ITerminalService.java
new file mode 100644
index 00000000000..a59f441cf78
--- /dev/null
+++ b/rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/ITerminalService.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Wind River Systems, 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:
+ * Martin Oberhuber (Wind River) - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.rse.internal.services.terminals;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import org.eclipse.rse.services.AbstractService;
+import org.eclipse.rse.services.IService;
+import org.eclipse.rse.services.clientserver.messages.SystemMessageException;
+
+/**
+ * Interface for getting Terminal Connections from a remote side, also known as
+ * terminal session with Streams.
+ *
+ * One ITerminalService instance is typically associated with one particular
+ * connection to a (potentially remote) system, such that the ITerminalService
+ * instance can also hold state data about that session, such as connected
+ * state, login and authentication information, configuration data or
+ * environment variables.
+ *
+ * Each
+ * {@link #launchTerminal(String, String, String[], String, String, IProgressMonitor)}
+ * invocation, however, acts as a factory method such that it creates a new
+ * (remote) process and associated {@link ITerminalShell} connection.
+ *
+ * @noimplement This interface is not intended to be implemented by clients.
+ * Clients must subclass the {@link AbstractService} class rather
+ * than implementing this interface directly.
+ *
+ * + * EXPERIMENTAL. This class or interface has been added as + * part of a work in progress. There is no guarantee that this API will work or + * that it will remain the same. Please do not use this API without consulting + * with the Target Management + * team. + *
+ * + * @since org.eclipse.rse.services 3.0 + * + */ +public interface ITerminalService extends IService { + + /** + * Launch a new terminal connection, also known as shell session with + * Streams. + * + * @param ptyType requested terminal type for the new Terminal. Since not + * all Terminal implementations support specifying the Terminal + * Type, there is no guarantee that a particular setting has any + * effect. Usenull
to fall back to a default
+ * terminal type.
+ * @param encoding Stream encoding to use for sending initial working
+ * directory and initial commandToRun, and to return on
+ * {@link ITerminalShell#getDefaultEncoding()} request. Use
+ * null
to fall back to a default encoding. The
+ * Terminal Service will make efforts to determine a proper
+ * default encoding on the remote side but this is not guaranteed
+ * to be correct.
+ * @param environment Array of environment variable Strings of the form
+ * "var=text". Since not all terminal implementations support the
+ * passing of environment variables, there is no guarantee that
+ * the created shell will actually have the specified environment
+ * set. Passing null
is allowed in order to
+ * specify that no specific environment needs to be passed.
+ * @param initialWorkingDirectory initial working directory or empty String
+ * ("") if not relevant. The remote shell will launch in a
+ * directory of its own choice in that case (typically a user's
+ * home directory).
+ * @param commandToRun initial command to send to the remote side.
+ * @param monitor Progress Monitor for monitoring and cancellation during
+ * connection creation.
+ * @return the terminal connection object. Note that the connection may not
+ * actually be usable in case the remote side allows opening a
+ * channel but immediately closes it again. In this case,
+ * {@link ITerminalShell#getInputStream()} will throw an
+ * exception or be closed from the very beginning.
+ * @throws SystemMessageException in case an error occurred or the user
+ * chose to cancel the operation via the progress monitor.
+ */
+ public ITerminalShell launchTerminal(String ptyType, String encoding, String[] environment, String initialWorkingDirectory, String commandToRun,
+ IProgressMonitor monitor) throws SystemMessageException;
+
+}
diff --git a/rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/ITerminalShell.java b/rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/ITerminalShell.java
new file mode 100644
index 00000000000..25eb8ae8ecf
--- /dev/null
+++ b/rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/ITerminalShell.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Wind River Systems, 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:
+ * Martin Oberhuber (Wind River) - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.rse.internal.services.terminals;
+
+import org.eclipse.rse.services.shells.IHostShell;
+
+/**
+ * Interface representing a terminal connection through Streams.
+ *
+ * Rather than the underlying {@link IBaseShell}, an ITerminalShell connection
+ * adds methods that describe the presentation of the data transmitted over its
+ * Streams, as well as methods like {@link #setTerminalSize(int, int)} to change
+ * the behavior of the presentation of this data. An instance of ITerminalShell
+ * is typically obtained from an {@link ITerminalService}.
+ *
+ * In RSE, a single remote shell instance can only either support the streamed
+ * ITerminalShell interface or the listener-based {@link IHostShell} interface,
+ * but not both. Note, though, that with the capabilities that an ITerminalShell
+ * has, it is always possible to adapt it to an IHostShell; this is typically
+ * not possible the other way round. We therefore recommend extenders of RSE
+ * that used to subclass IHostShell to move to the new IBaseShell /
+ * ITerminalShell APIs eventually, if they can.
+ *
+ * @noimplement This interface is not intended to be implemented by clients.
+ * Clients must subclass the provided {@link AbstractTerminalShell}
+ * or {@link TerminalShellDecorator} classes rather than
+ * implementing this interface directly.
+ *
+ * + * EXPERIMENTAL. This class or interface has been added as + * part of a work in progress. There is no guarantee that this API will work or + * that it will remain the same. Please do not use this API without consulting + * with the Target Management + * team. + *
+ * + * @see IBaseShell + * @see ITerminalService + * @see AbstractTerminalShell + * @see TerminalShellDecorator + * @since org.eclipse.rse.services 3.0 + */ +public interface ITerminalShell extends IBaseShell { + + /** + * Get the Terminal Type that's expected on this connection. + * + * The terminal type may be specified by the client when constructing a + * concrete instance of an ITerminalShell, or a remote side may actually + * expect a particular terminal type to be present. + * + * @return the terminal type expected by the remote side to properly render + * the Streams associated with this Terminal, ornull
+ * if the ITerminalShell does not know what kind of Terminal Type is
+ * expected.
+ */
+ public String getPtyType();
+
+ /**
+ * Return the default encoding that the terminal service had specified when
+ * creating this terminal connection, or that's known from the remote side
+ * to be expected. This is not necessarily known or accurate, and may be
+ * null
.
+ *
+ * TODO I'm not actually sure if this method is a good idea. Perhaps we
+ * should use the IAdaptable mechanism for dealing with encodings, since our
+ * shells basically deal with binary data only.
+ *
+ * @return the specified default encoding, or null
if
+ * unknown.
+ */
+ public String getDefaultEncoding();
+
+ /**
+ * Notify the remote site that the size of the terminal has changed. There
+ * is no guarantee that the remote side is actually capable of changing the
+ * Terminal size.
+ *
+ * @param newWidth
+ * @param newHeight
+ */
+ void setTerminalSize(int newWidth, int newHeight);
+
+ /**
+ * Test if local echo is needed on this terminal connection. Clients are
+ * expected to return false
if in doubt.
+ *
+ * @return true
if a local echo is needed.
+ */
+ boolean isLocalEcho();
+
+}
diff --git a/rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/ProcessBaseShell.java b/rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/ProcessBaseShell.java
new file mode 100644
index 00000000000..d569d7837ef
--- /dev/null
+++ b/rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/ProcessBaseShell.java
@@ -0,0 +1,148 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Wind River Systems, 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:
+ * Martin Oberhuber (Wind River) - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.rse.internal.services.terminals;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.eclipse.core.runtime.PlatformObject;
+
+/**
+ * A wrapper for Java {@link Process} objects to give more convenient access to
+ * them through the {@link IBaseShell} interface.
+ *
+ * + * EXPERIMENTAL. This class or interface has been added as + * part of a work in progress. There is no guarantee that this API will work or + * that it will remain the same. Please do not use this API without consulting + * with the Target Management + * team. + *
+ * + * @since org.eclipse.rse.services 3.0 + */ +public class ProcessBaseShell extends PlatformObject implements IBaseShell { + + /** + * The underlying Process instance. + */ + protected final Process fProcess; + + /** + * Constructor. + * + * @param p the Process to wrap with this IBaseShell interface. + */ + public ProcessBaseShell(Process p) { + fProcess = p; + } + + /** + * Forcefully terminate the underlying Process through + * {@link Process#destroy()}. Subclasses may want to override this behavior + * by trying to terminate the underlying Process in a cleaner way. + * + * @see IBaseShell#exit() + */ + public void exit() { + fProcess.destroy(); + } + + public int exitValue() { + return fProcess.exitValue(); + } + + public InputStream getErrorStream() { + return fProcess.getErrorStream(); + } + + public InputStream getInputStream() { + return fProcess.getInputStream(); + } + + public OutputStream getOutputStream() { + return fProcess.getOutputStream(); + } + + /** + * Check if the underlying Process is still active. Does not check whether + * the Streams for the Process have been closed by the client, since this + * does not influence the process active state anyways. + * + * @see IBaseShell#isActive() + */ + public boolean isActive() { + try { + fProcess.exitValue(); + } catch (IllegalThreadStateException e) { + return true; + } + return false; + } + + /** + * A Watchdog Thread, to interrupt other Threads after a given time unless a + * specified condition is met. + * + * Sleeps for a given time, and upon wakeup checks if a condition is met. If + * not, the specified Thread is interrupted. + */ + private abstract static class Watchdog extends Thread { + private final Thread fThreadToWatch; + private long fTimeout; + + public Watchdog(Thread threadToWatch, long timeout) { + fThreadToWatch = threadToWatch; + fTimeout = timeout; + } + + protected abstract boolean conditionDone(); + + public void run() { + try { + sleep(fTimeout); + } catch (InterruptedException e) { + /* ignore */ + } finally { + if (!conditionDone()) { + fThreadToWatch.interrupt(); + } + } + } + } + + public boolean waitFor(long timeout) throws InterruptedException { + boolean active = isActive(); + if (active) { + Thread watchdog = null; + if (timeout > 0) { + watchdog = new Watchdog(Thread.currentThread(), timeout) { + protected boolean conditionDone() { + return !isActive(); + } + }; + watchdog.start(); + } + try { + fProcess.waitFor(); + } catch (InterruptedException e) { + /* ignore */ + } + if (watchdog != null) { + watchdog.interrupt(); + } + active = isActive(); + } + return active; + } + +} diff --git a/rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/TerminalShellDecorator.java b/rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/TerminalShellDecorator.java new file mode 100644 index 00000000000..492ce473fdf --- /dev/null +++ b/rse/plugins/org.eclipse.rse.services/src/org/eclipse/rse/internal/services/terminals/TerminalShellDecorator.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems, 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: + * Martin Oberhuber (Wind River) - initial API and implementation + *******************************************************************************/ + +package org.eclipse.rse.internal.services.terminals; + +/** + * Abstract base class for clients to decorate an ITerminalShell instance they + * have with additional functionality. + * + * Typically, such additional functionality can be provided either by additional + * knowledge about the underlying system or process, or by analyzing the input + * and output streams for some well-known data that gives such additional + * knowledge. + * + *+ * EXPERIMENTAL. This class or interface has been added as + * part of a work in progress. There is no guarantee that this API will work or + * that it will remain the same. Please do not use this API without consulting + * with the Target Management + * team. + *
+ * + * @since org.eclipse.rse.services 3.0 + */ +public abstract class TerminalShellDecorator extends BaseShellDecorator implements ITerminalShell { + + public TerminalShellDecorator(ITerminalShell delegate) { + super(delegate); + } + + protected ITerminalShell getDelegate() { + return (ITerminalShell) fDelegate; + } + + public String getPtyType() { + return getDelegate().getPtyType(); + } + + public String getDefaultEncoding() { + return getDelegate().getDefaultEncoding(); + } + + public boolean isLocalEcho() { + return getDelegate().isLocalEcho(); + } + + public void setTerminalSize(int newWidth, int newHeight) { + getDelegate().setTerminalSize(newWidth, newHeight); + } + +}