Browse Source
The RemoteSession interface operates like a simplified version of java.lang.Runtime with a single exec method (and a disconnect method). It returns a java.lang.Process, which should begin execution immediately. Note that this greatly simplifies the interface for running commands. There is no longer a connect method, and most implementations will contain the bulk of their code inside Process.exec, or a constructor called by Process.exec. (See the revised implementations of JschSession and ExtSession.) Implementations can now configure their connections properly without either ignoring the proper use of the interface or trying to adhere to an overly strict interface with odd rules about what methods are called first. For example, Jsch needs to create the output stream before executing, which it now does in the process constructor. These changes should make it much easier to add alternate session implementations in the future. Also-by: John D Eblen <jdeblen@comcast.net> Bug: 336749 CQ: 5004 Change-Id: Iece43632086afadf175af6638255041ccaf2bfbb Signed-off-by: Chris Aniszczyk <caniszczyk@gmail.com>stable-0.12
Roland Schulz
14 years ago
committed by
Chris Aniszczyk
8 changed files with 451 additions and 286 deletions
@ -0,0 +1,237 @@
|
||||
/* |
||||
* Copyright (C) 2009, Constantine Plotnikov <constantine.plotnikov@gmail.com> |
||||
* Copyright (C) 2008-2009, Google Inc. |
||||
* Copyright (C) 2009, Google, Inc. |
||||
* Copyright (C) 2009, JetBrains s.r.o. |
||||
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com> |
||||
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> |
||||
* and other copyright owners as documented in the project's IP log. |
||||
* |
||||
* This program and the accompanying materials are made available |
||||
* under the terms of the Eclipse Distribution License v1.0 which |
||||
* accompanies this distribution, is reproduced below, and is |
||||
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||
* |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or |
||||
* without modification, are permitted provided that the following |
||||
* conditions are met: |
||||
* |
||||
* - Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* - Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following |
||||
* disclaimer in the documentation and/or other materials provided |
||||
* with the distribution. |
||||
* |
||||
* - Neither the name of the Eclipse Foundation, Inc. nor the |
||||
* names of its contributors may be used to endorse or promote |
||||
* products derived from this software without specific prior |
||||
* written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package org.eclipse.jgit.transport; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.io.OutputStream; |
||||
import java.io.PipedInputStream; |
||||
import java.io.PipedOutputStream; |
||||
|
||||
import org.eclipse.jgit.errors.TransportException; |
||||
import org.eclipse.jgit.util.io.StreamCopyThread; |
||||
|
||||
import com.jcraft.jsch.Channel; |
||||
import com.jcraft.jsch.ChannelExec; |
||||
import com.jcraft.jsch.JSchException; |
||||
import com.jcraft.jsch.Session; |
||||
|
||||
/** |
||||
* Run remote commands using Jsch. |
||||
* <p> |
||||
* This class is the default session implementation using Jsch. Note that |
||||
* {@link JschConfigSessionFactory} is used to create the actual session passed |
||||
* to the constructor. |
||||
*/ |
||||
public class JschSession implements RemoteSession { |
||||
private final Session sock; |
||||
private final URIish uri; |
||||
|
||||
/** |
||||
* Create a new session object by passing the real Jsch session and the URI |
||||
* information. |
||||
* |
||||
* @param session |
||||
* the real Jsch session created elsewhere. |
||||
* @param uri |
||||
* the URI information for the remote connection |
||||
*/ |
||||
public JschSession(final Session session, URIish uri) { |
||||
sock = session; |
||||
this.uri = uri; |
||||
} |
||||
|
||||
public Process exec(String command, int timeout) throws IOException { |
||||
return new JschProcess(command, timeout); |
||||
} |
||||
|
||||
public void disconnect() { |
||||
if (sock.isConnected()) |
||||
sock.disconnect(); |
||||
} |
||||
|
||||
/** |
||||
* A kludge to allow {@link TransportSftp} to get an Sftp channel from Jsch. |
||||
* Ideally, this method would be generic, which would require implementing |
||||
* generic Sftp channel operations in the RemoteSession class. |
||||
* |
||||
* @return a channel suitable for Sftp operations. |
||||
* @throws JSchException |
||||
* on problems getting the channel. |
||||
*/ |
||||
public Channel getSftpChannel() throws JSchException { |
||||
return sock.openChannel("sftp"); |
||||
} |
||||
|
||||
/** |
||||
* Implementation of Process for running a single command using Jsch. |
||||
* <p> |
||||
* Uses the Jsch session to do actual command execution and manage the |
||||
* execution. |
||||
*/ |
||||
private class JschProcess extends Process { |
||||
private ChannelExec channel; |
||||
|
||||
private final int timeout; |
||||
|
||||
private InputStream inputStream; |
||||
|
||||
private OutputStream outputStream; |
||||
|
||||
private InputStream errStream; |
||||
|
||||
/** |
||||
* Opens a channel on the session ("sock") for executing the given |
||||
* command, opens streams, and starts command execution. |
||||
* |
||||
* @param commandName |
||||
* the command to execute |
||||
* @param tms |
||||
* the timeout value, in seconds, for the command. |
||||
* @throws TransportException |
||||
* on problems opening a channel or connecting to the remote |
||||
* host |
||||
* @throws IOException |
||||
* on problems opening streams |
||||
*/ |
||||
private JschProcess(final String commandName, int tms) |
||||
throws TransportException, IOException { |
||||
timeout = tms; |
||||
try { |
||||
channel = (ChannelExec) sock.openChannel("exec"); |
||||
channel.setCommand(commandName); |
||||
setupStreams(); |
||||
channel.connect(timeout > 0 ? timeout * 1000 : 0); |
||||
if (!channel.isConnected()) |
||||
throw new TransportException(uri, "connection failed"); |
||||
} catch (JSchException e) { |
||||
throw new TransportException(uri, e.getMessage(), e); |
||||
} |
||||
} |
||||
|
||||
private void setupStreams() throws IOException { |
||||
inputStream = channel.getInputStream(); |
||||
|
||||
// JSch won't let us interrupt writes when we use our InterruptTimer
|
||||
// to break out of a long-running write operation. To work around
|
||||
// that we spawn a background thread to shuttle data through a pipe,
|
||||
// as we can issue an interrupted write out of that. Its slower, so
|
||||
// we only use this route if there is a timeout.
|
||||
final OutputStream out = channel.getOutputStream(); |
||||
if (timeout <= 0) { |
||||
outputStream = out; |
||||
} else { |
||||
final PipedInputStream pipeIn = new PipedInputStream(); |
||||
final StreamCopyThread copier = new StreamCopyThread(pipeIn, |
||||
out); |
||||
final PipedOutputStream pipeOut = new PipedOutputStream(pipeIn) { |
||||
@Override |
||||
public void flush() throws IOException { |
||||
super.flush(); |
||||
copier.flush(); |
||||
} |
||||
|
||||
@Override |
||||
public void close() throws IOException { |
||||
super.close(); |
||||
try { |
||||
copier.join(timeout * 1000); |
||||
} catch (InterruptedException e) { |
||||
// Just wake early, the thread will terminate
|
||||
// anyway.
|
||||
} |
||||
} |
||||
}; |
||||
copier.start(); |
||||
outputStream = pipeOut; |
||||
} |
||||
|
||||
errStream = channel.getErrStream(); |
||||
} |
||||
|
||||
@Override |
||||
public InputStream getInputStream() { |
||||
return inputStream; |
||||
} |
||||
|
||||
@Override |
||||
public OutputStream getOutputStream() { |
||||
return outputStream; |
||||
} |
||||
|
||||
@Override |
||||
public InputStream getErrorStream() { |
||||
return errStream; |
||||
} |
||||
|
||||
@Override |
||||
public int exitValue() { |
||||
if (isRunning()) |
||||
throw new IllegalStateException(); |
||||
return channel.getExitStatus(); |
||||
} |
||||
|
||||
private boolean isRunning() { |
||||
return channel.getExitStatus() < 0 && channel.isConnected(); |
||||
} |
||||
|
||||
@Override |
||||
public void destroy() { |
||||
if (channel.isConnected()) |
||||
channel.disconnect(); |
||||
} |
||||
|
||||
@Override |
||||
public int waitFor() throws InterruptedException { |
||||
while (isRunning()) |
||||
Thread.sleep(100); |
||||
return exitValue(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,86 @@
|
||||
/* |
||||
* Copyright (C) 2009, Constantine Plotnikov <constantine.plotnikov@gmail.com> |
||||
* Copyright (C) 2008-2009, Google Inc. |
||||
* Copyright (C) 2009, Google, Inc. |
||||
* Copyright (C) 2009, JetBrains s.r.o. |
||||
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com> |
||||
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> |
||||
* and other copyright owners as documented in the project's IP log. |
||||
* |
||||
* This program and the accompanying materials are made available |
||||
* under the terms of the Eclipse Distribution License v1.0 which |
||||
* accompanies this distribution, is reproduced below, and is |
||||
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||
* |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or |
||||
* without modification, are permitted provided that the following |
||||
* conditions are met: |
||||
* |
||||
* - Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* - Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following |
||||
* disclaimer in the documentation and/or other materials provided |
||||
* with the distribution. |
||||
* |
||||
* - Neither the name of the Eclipse Foundation, Inc. nor the |
||||
* names of its contributors may be used to endorse or promote |
||||
* products derived from this software without specific prior |
||||
* written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package org.eclipse.jgit.transport; |
||||
|
||||
import java.io.IOException; |
||||
|
||||
/** |
||||
* Create a remote "session" for executing remote commands. |
||||
* <p> |
||||
* Clients should subclass RemoteSession to create an alternate way for JGit to |
||||
* execute remote commands. (The client application may already have this |
||||
* functionality available.) Note that this class is just a factory for creating |
||||
* remote processes. If the application already has a persistent connection to |
||||
* the remote machine, RemoteSession may do nothing more than return a new |
||||
* RemoteProcess when exec is called. |
||||
*/ |
||||
public interface RemoteSession { |
||||
/** |
||||
* Generate a new remote process to execute the given command. This function |
||||
* should also start execution and may need to create the streams prior to |
||||
* execution. |
||||
* @param commandName |
||||
* command to execute |
||||
* @param timeout |
||||
* timeout value, in seconds, for command execution |
||||
* @return a new remote process |
||||
* @throws IOException |
||||
* may be thrown in several cases. For example, on problems |
||||
* opening input or output streams or on problems connecting or |
||||
* communicating with the remote host. For the latter two cases, |
||||
* a TransportException may be thrown (a subclass of |
||||
* IOException). |
||||
*/ |
||||
public Process exec(String commandName, int timeout) throws IOException; |
||||
|
||||
/** |
||||
* Disconnect the remote session |
||||
*/ |
||||
public void disconnect(); |
||||
} |
Loading…
Reference in new issue