1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-04 07:35:24 +02:00

[170910][api] Add API for Stream-based shells and terminals in RSE

This commit is contained in:
Martin Oberhuber 2008-04-11 13:36:45 +00:00
parent 5253094809
commit 5bb00b5009
8 changed files with 730 additions and 0 deletions

View file

@ -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,

View file

@ -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.
*
* <p>
* <strong>EXPERIMENTAL</strong>. 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 <a href="http://www.eclipse.org/dsdp/tm/">Target Management</a>
* team.
* </p>
*
* @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;
}
}

View file

@ -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.
*
* <p>
* <strong>EXPERIMENTAL</strong>. 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 <a href="http://www.eclipse.org/dsdp/tm/">Target Management</a>
* team.
* </p>
*
* @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;
}
}

View file

@ -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}.
*
* <p>
* <strong>EXPERIMENTAL</strong>. 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 <a href="http://www.eclipse.org/dsdp/tm/">Target Management</a>
* team.
* </p>
*
* @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 return <code>null</code> 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 <code>null</code> 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 <code>true</code> 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 <code>false</code>, 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 <code>true</code> if the Shell is still active after waiting
* (e.g. because the timeout has elapsed); <code>false</code> 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;
}

View file

@ -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.
*
* <p>
* <strong>EXPERIMENTAL</strong>. 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 <a href="http://www.eclipse.org/dsdp/tm/">Target Management</a>
* team.
* </p>
*
* @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. Use <code>null</code> 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
* <code>null</code> 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 <code>null</code> 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;
}

View file

@ -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.
*
* <p>
* <strong>EXPERIMENTAL</strong>. 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 <a href="http://www.eclipse.org/dsdp/tm/">Target Management</a>
* team.
* </p>
*
* @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, or <code>null</code>
* 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
* <code>null</code>.
*
* 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 <code>null</code> 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 <code>false</code> if in doubt.
*
* @return <code>true</code> if a local echo is needed.
*/
boolean isLocalEcho();
}

View file

@ -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.
*
* <p>
* <strong>EXPERIMENTAL</strong>. 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 <a href="http://www.eclipse.org/dsdp/tm/">Target Management</a>
* team.
* </p>
*
* @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;
}
}

View file

@ -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.
*
* <p>
* <strong>EXPERIMENTAL</strong>. 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 <a href="http://www.eclipse.org/dsdp/tm/">Target Management</a>
* team.
* </p>
*
* @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);
}
}