]> source.dussan.org Git - jgit.git/commitdiff
Create RemoteSession interface 83/2583/5
authorRoland Schulz <roland@utk.edu>
Sat, 5 Mar 2011 00:50:14 +0000 (19:50 -0500)
committerChris Aniszczyk <caniszczyk@gmail.com>
Wed, 23 Mar 2011 15:48:08 +0000 (10:48 -0500)
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>
org.eclipse.jgit/src/org/eclipse/jgit/transport/DefaultSshSessionFactory.java
org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java [new file with mode: 0644]
org.eclipse.jgit/src/org/eclipse/jgit/transport/JschSession.java [new file with mode: 0644]
org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteSession.java [new file with mode: 0644]
org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConfigSessionFactory.java [deleted file]
org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java
org.eclipse.jgit/src/org/eclipse/jgit/transport/SshTransport.java
org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java

index 53c1a2ce3adbf88a5afb36a08e1ad452e3d0e0b1..70128077d2a7b3051ef23f965b2dcd371d8e1a2a 100644 (file)
@@ -58,7 +58,7 @@ import com.jcraft.jsch.Session;
  * If user interactivity is required by SSH (e.g. to obtain a password), the
  * connection will immediately fail.
  */
-class DefaultSshSessionFactory extends SshConfigSessionFactory {
+class DefaultSshSessionFactory extends JschConfigSessionFactory {
        protected void configure(final OpenSshConfig.Host hc, final Session session) {
                // No additional configuration required.
        }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java
new file mode 100644 (file)
index 0000000..b1f2324
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * 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.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.ConnectException;
+import java.net.UnknownHostException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.util.FS;
+
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+import com.jcraft.jsch.UserInfo;
+
+/**
+ * The base session factory that loads known hosts and private keys from
+ * <code>$HOME/.ssh</code>.
+ * <p>
+ * This is the default implementation used by JGit and provides most of the
+ * compatibility necessary to match OpenSSH, a popular implementation of SSH
+ * used by C Git.
+ * <p>
+ * The factory does not provide UI behavior. Override the method
+ * {@link #configure(org.eclipse.jgit.transport.OpenSshConfig.Host, Session)}
+ * to supply appropriate {@link UserInfo} to the session.
+ */
+public abstract class JschConfigSessionFactory extends SshSessionFactory {
+       private final Map<String, JSch> byIdentityFile = new HashMap<String, JSch>();
+
+       private JSch defaultJSch;
+
+       private OpenSshConfig config;
+
+       @Override
+       public synchronized RemoteSession getSession(URIish uri,
+                       CredentialsProvider credentialsProvider, FS fs, int tms)
+                       throws TransportException {
+
+               String user = uri.getUser();
+               final String pass = uri.getPass();
+               String host = uri.getHost();
+               int port = uri.getPort();
+
+               try {
+                       if (config == null)
+                               config = OpenSshConfig.get(fs);
+
+                       final OpenSshConfig.Host hc = config.lookup(host);
+                       host = hc.getHostName();
+                       if (port <= 0)
+                               port = hc.getPort();
+                       if (user == null)
+                               user = hc.getUser();
+
+                       final Session session = createSession(hc, user, host, port, fs);
+                       if (pass != null)
+                               session.setPassword(pass);
+                       final String strictHostKeyCheckingPolicy = hc
+                                       .getStrictHostKeyChecking();
+                       if (strictHostKeyCheckingPolicy != null)
+                               session.setConfig("StrictHostKeyChecking",
+                                               strictHostKeyCheckingPolicy);
+                       final String pauth = hc.getPreferredAuthentications();
+                       if (pauth != null)
+                               session.setConfig("PreferredAuthentications", pauth);
+                       if (credentialsProvider != null
+                               && (!hc.isBatchMode() || !credentialsProvider.isInteractive())) {
+                               session.setUserInfo(new CredentialsProviderUserInfo(session,
+                                               credentialsProvider));
+                       }
+                       configure(hc, session);
+
+                       if (!session.isConnected())
+                               session.connect(tms);
+
+                       return new JschSession(session, uri);
+
+               } catch (JSchException je) {
+                       final Throwable c = je.getCause();
+                       if (c instanceof UnknownHostException)
+                               throw new TransportException(uri, JGitText.get().unknownHost);
+                       if (c instanceof ConnectException)
+                               throw new TransportException(uri, c.getMessage());
+                       throw new TransportException(uri, je.getMessage(), je);
+               }
+
+       }
+
+       /**
+        * Create a new remote session for the requested address.
+        * 
+        * @param hc
+        *            host configuration
+        * @param user
+        *            login to authenticate as.
+        * @param host
+        *            server name to connect to.
+        * @param port
+        *            port number of the SSH daemon (typically 22).
+        * @param fs
+        *            the file system abstraction which will be necessary to
+        *            perform certain file system operations.
+        * @return new session instance, but otherwise unconfigured.
+        * @throws JSchException
+        *             the session could not be created.
+        */
+       protected Session createSession(final OpenSshConfig.Host hc,
+                       final String user, final String host, final int port, FS fs)
+                       throws JSchException {
+               return getJSch(hc, fs).getSession(user, host, port);
+       }
+
+       /**
+        * Provide additional configuration for the session based on the host
+        * information. This method could be used to supply {@link UserInfo}.
+        *
+        * @param hc
+        *            host configuration
+        * @param session
+        *            session to configure
+        */
+       protected abstract void configure(OpenSshConfig.Host hc, Session session);
+
+       /**
+        * Obtain the JSch used to create new sessions.
+        *
+        * @param hc
+        *            host configuration
+        * @param fs
+        *            the file system abstraction which will be necessary to
+        *            perform certain file system operations.
+        * @return the JSch instance to use.
+        * @throws JSchException
+        *             the user configuration could not be created.
+        */
+       protected JSch getJSch(final OpenSshConfig.Host hc, FS fs) throws JSchException {
+               if (defaultJSch == null) {
+                       defaultJSch = createDefaultJSch(fs);
+                       for (Object name : defaultJSch.getIdentityNames())
+                               byIdentityFile.put((String) name, defaultJSch);
+               }
+
+               final File identityFile = hc.getIdentityFile();
+               if (identityFile == null)
+                       return defaultJSch;
+
+               final String identityKey = identityFile.getAbsolutePath();
+               JSch jsch = byIdentityFile.get(identityKey);
+               if (jsch == null) {
+                       jsch = new JSch();
+                       jsch.setHostKeyRepository(defaultJSch.getHostKeyRepository());
+                       jsch.addIdentity(identityKey);
+                       byIdentityFile.put(identityKey, jsch);
+               }
+               return jsch;
+       }
+
+       /**
+        * @param fs
+        *            the file system abstraction which will be necessary to
+        *            perform certain file system operations.
+        * @return the new default JSch implementation.
+        * @throws JSchException
+        *             known host keys cannot be loaded.
+        */
+       protected JSch createDefaultJSch(FS fs) throws JSchException {
+               final JSch jsch = new JSch();
+               knownHosts(jsch, fs);
+               identities(jsch, fs);
+               return jsch;
+       }
+
+       private static void knownHosts(final JSch sch, FS fs) throws JSchException {
+               final File home = fs.userHome();
+               if (home == null)
+                       return;
+               final File known_hosts = new File(new File(home, ".ssh"), "known_hosts");
+               try {
+                       final FileInputStream in = new FileInputStream(known_hosts);
+                       try {
+                               sch.setKnownHosts(in);
+                       } finally {
+                               in.close();
+                       }
+               } catch (FileNotFoundException none) {
+                       // Oh well. They don't have a known hosts in home.
+               } catch (IOException err) {
+                       // Oh well. They don't have a known hosts in home.
+               }
+       }
+
+       private static void identities(final JSch sch, FS fs) {
+               final File home = fs.userHome();
+               if (home == null)
+                       return;
+               final File sshdir = new File(home, ".ssh");
+               if (sshdir.isDirectory()) {
+                       loadIdentity(sch, new File(sshdir, "identity"));
+                       loadIdentity(sch, new File(sshdir, "id_rsa"));
+                       loadIdentity(sch, new File(sshdir, "id_dsa"));
+               }
+       }
+
+       private static void loadIdentity(final JSch sch, final File priv) {
+               if (priv.isFile()) {
+                       try {
+                               sch.addIdentity(priv.getAbsolutePath());
+                       } catch (JSchException e) {
+                               // Instead, pretend the key doesn't exist.
+                       }
+               }
+       }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschSession.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschSession.java
new file mode 100644 (file)
index 0000000..9dc0da6
--- /dev/null
@@ -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();
+               }
+       }
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteSession.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteSession.java
new file mode 100644 (file)
index 0000000..5a73cf5
--- /dev/null
@@ -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();
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConfigSessionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConfigSessionFactory.java
deleted file mode 100644 (file)
index 99e7b83..0000000
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * 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.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.eclipse.jgit.util.FS;
-
-import com.jcraft.jsch.JSch;
-import com.jcraft.jsch.JSchException;
-import com.jcraft.jsch.Session;
-import com.jcraft.jsch.UserInfo;
-
-/**
- * The base session factory that loads known hosts and private keys from
- * <code>$HOME/.ssh</code>.
- * <p>
- * This is the default implementation used by JGit and provides most of the
- * compatibility necessary to match OpenSSH, a popular implementation of SSH
- * used by C Git.
- * <p>
- * The factory does not provide UI behavior. Override the method
- * {@link #configure(org.eclipse.jgit.transport.OpenSshConfig.Host, Session)}
- * to supply appropriate {@link UserInfo} to the session.
- */
-public abstract class SshConfigSessionFactory extends SshSessionFactory {
-       private final Map<String, JSch> byIdentityFile = new HashMap<String, JSch>();
-
-       private JSch defaultJSch;
-
-       private OpenSshConfig config;
-
-       @Override
-       public synchronized Session getSession(String user, String pass,
-                       String host, int port, CredentialsProvider credentialsProvider,
-                       FS fs) throws JSchException {
-               if (config == null)
-                       config = OpenSshConfig.get(fs);
-
-               final OpenSshConfig.Host hc = config.lookup(host);
-               host = hc.getHostName();
-               if (port <= 0)
-                       port = hc.getPort();
-               if (user == null)
-                       user = hc.getUser();
-
-               final Session session = createSession(hc, user, host, port, fs);
-               if (pass != null)
-                       session.setPassword(pass);
-               final String strictHostKeyCheckingPolicy = hc
-                               .getStrictHostKeyChecking();
-               if (strictHostKeyCheckingPolicy != null)
-                       session.setConfig("StrictHostKeyChecking",
-                                       strictHostKeyCheckingPolicy);
-               final String pauth = hc.getPreferredAuthentications();
-               if (pauth != null)
-                       session.setConfig("PreferredAuthentications", pauth);
-               if (credentialsProvider != null
-                               && (!hc.isBatchMode() || !credentialsProvider.isInteractive())) {
-                       session.setUserInfo(new CredentialsProviderUserInfo(session,
-                                       credentialsProvider));
-               }
-               configure(hc, session);
-               return session;
-       }
-
-       /**
-        * Create a new JSch session for the requested address.
-        *
-        * @param hc
-        *            host configuration
-        * @param user
-        *            login to authenticate as.
-        * @param host
-        *            server name to connect to.
-        * @param port
-        *            port number of the SSH daemon (typically 22).
-        * @param fs
-        *            the file system abstraction which will be necessary to
-        *            perform certain file system operations.
-        * @return new session instance, but otherwise unconfigured.
-        * @throws JSchException
-        *             the session could not be created.
-        */
-       protected Session createSession(final OpenSshConfig.Host hc,
-                       final String user, final String host, final int port, FS fs)
-                       throws JSchException {
-               return getJSch(hc, fs).getSession(user, host, port);
-       }
-
-       /**
-        * Provide additional configuration for the session based on the host
-        * information. This method could be used to supply {@link UserInfo}.
-        *
-        * @param hc
-        *            host configuration
-        * @param session
-        *            session to configure
-        */
-       protected abstract void configure(OpenSshConfig.Host hc, Session session);
-
-       /**
-        * Obtain the JSch used to create new sessions.
-        *
-        * @param hc
-        *            host configuration
-        * @param fs
-        *            the file system abstraction which will be necessary to
-        *            perform certain file system operations.
-        * @return the JSch instance to use.
-        * @throws JSchException
-        *             the user configuration could not be created.
-        */
-       protected JSch getJSch(final OpenSshConfig.Host hc, FS fs) throws JSchException {
-               if (defaultJSch == null) {
-                       defaultJSch = createDefaultJSch(fs);
-                       for (Object name : defaultJSch.getIdentityNames()) {
-                               byIdentityFile.put((String) name, defaultJSch);
-                       }
-               }
-
-               final File identityFile = hc.getIdentityFile();
-               if (identityFile == null) {
-                       return defaultJSch;
-               }
-
-               final String identityKey = identityFile.getAbsolutePath();
-               JSch jsch = byIdentityFile.get(identityKey);
-               if (jsch == null) {
-                       jsch = new JSch();
-                       jsch.setHostKeyRepository(defaultJSch.getHostKeyRepository());
-                       jsch.addIdentity(identityKey);
-                       byIdentityFile.put(identityKey, jsch);
-               }
-               return jsch;
-       }
-
-       /**
-        * @param fs
-        *            the file system abstraction which will be necessary to
-        *            perform certain file system operations.
-        * @return the new default JSch implementation.
-        * @throws JSchException
-        *             known host keys cannot be loaded.
-        */
-       protected JSch createDefaultJSch(FS fs) throws JSchException {
-               final JSch jsch = new JSch();
-               knownHosts(jsch, fs);
-               identities(jsch, fs);
-               return jsch;
-       }
-
-       private static void knownHosts(final JSch sch, FS fs) throws JSchException {
-               final File home = fs.userHome();
-               if (home == null)
-                       return;
-               final File known_hosts = new File(new File(home, ".ssh"), "known_hosts");
-               try {
-                       final FileInputStream in = new FileInputStream(known_hosts);
-                       try {
-                               sch.setKnownHosts(in);
-                       } finally {
-                               in.close();
-                       }
-               } catch (FileNotFoundException none) {
-                       // Oh well. They don't have a known hosts in home.
-               } catch (IOException err) {
-                       // Oh well. They don't have a known hosts in home.
-               }
-       }
-
-       private static void identities(final JSch sch, FS fs) {
-               final File home = fs.userHome();
-               if (home == null)
-                       return;
-               final File sshdir = new File(home, ".ssh");
-               if (sshdir.isDirectory()) {
-                       loadIdentity(sch, new File(sshdir, "identity"));
-                       loadIdentity(sch, new File(sshdir, "id_rsa"));
-                       loadIdentity(sch, new File(sshdir, "id_dsa"));
-               }
-       }
-
-       private static void loadIdentity(final JSch sch, final File priv) {
-               if (priv.isFile()) {
-                       try {
-                               sch.addIdentity(priv.getAbsolutePath());
-                       } catch (JSchException e) {
-                               // Instead, pretend the key doesn't exist.
-                       }
-               }
-       }
-}
index 34aa3dbd24ba1239a72cd2248070b5baa6538839..a1aeceb2fd7721afa5c484b2fa67a228e7e822c8 100644 (file)
 
 package org.eclipse.jgit.transport;
 
+import org.eclipse.jgit.errors.TransportException;
 import org.eclipse.jgit.util.FS;
 
-import com.jcraft.jsch.JSchException;
-import com.jcraft.jsch.Session;
-
 /**
  * Creates and destroys SSH connections to a remote system.
  * <p>
@@ -56,9 +54,9 @@ import com.jcraft.jsch.Session;
  * communicating with the end-user as well as reading their personal SSH
  * configuration settings, such as known hosts and private keys.
  * <p>
- * A {@link Session} must be returned to the factory that created it. Callers
- * are encouraged to retain the SshSessionFactory for the duration of the period
- * they are using the Session.
+ * A {@link RemoteSession} must be returned to the factory that created it.
+ * Callers are encouraged to retain the SshSessionFactory for the duration of
+ * the period they are using the Session.
  */
 public abstract class SshSessionFactory {
        private static SshSessionFactory INSTANCE = new DefaultSshSessionFactory();
@@ -68,7 +66,7 @@ public abstract class SshSessionFactory {
         * <p>
         * A factory is always available. By default the factory will read from the
         * user's <code>$HOME/.ssh</code> and assume OpenSSH compatibility.
-        *
+        * 
         * @return factory the current factory for this JVM.
         */
        public static SshSessionFactory getInstance() {
@@ -98,42 +96,32 @@ public abstract class SshSessionFactory {
         * The caller must connect the session by invoking <code>connect()</code>
         * if it has not already been connected.
         *
-        * @param user
-        *            username to authenticate as. If null a reasonable default must
-        *            be selected by the implementation. This may be
-        *            <code>System.getProperty("user.name")</code>.
-        * @param pass
-        *            optional user account password or passphrase. If not null a
-        *            UserInfo that supplies this value to the SSH library will be
-        *            configured.
-        * @param host
-        *            hostname (or IP address) to connect to. Must not be null.
-        * @param port
-        *            port number the server is listening for connections on. May be <=
-        *            0 to indicate the IANA registered port of 22 should be used.
+        * @param uri
+        *            URI information about the remote host
         * @param credentialsProvider
         *            provider to support authentication, may be null.
         * @param fs
         *            the file system abstraction which will be necessary to
         *            perform certain file system operations.
+        * @param tms
+        *            Timeout value, in milliseconds.
         * @return a session that can contact the remote host.
-        * @throws JSchException
+        * @throws TransportException
         *             the session could not be created.
         */
-       public abstract Session getSession(String user, String pass, String host,
-                       int port, CredentialsProvider credentialsProvider, FS fs)
-                       throws JSchException;
+       public abstract RemoteSession getSession(URIish uri,
+                       CredentialsProvider credentialsProvider, FS fs, int tms)
+                       throws TransportException;
 
        /**
         * Close (or recycle) a session to a host.
         *
         * @param session
         *            a session previously obtained from this factory's
-        *            {@link #getSession(String,String, String, int, CredentialsProvider, FS)}
+        *            {@link #getSession(URIish, CredentialsProvider, FS, int)}
         *            method.
         */
-       public void releaseSession(final Session session) {
-               if (session.isConnected())
-                       session.disconnect();
+       public void releaseSession(final RemoteSession session) {
+               session.disconnect();
        }
 }
index 81d233f1ed883da1a6cb0b78126a62a4f1450780..46de01c2ff35890de2f18f330fe070b3602fc9ae 100644 (file)
 
 package org.eclipse.jgit.transport;
 
-import java.net.ConnectException;
-import java.net.UnknownHostException;
-
 import org.eclipse.jgit.JGitText;
 import org.eclipse.jgit.errors.TransportException;
 import org.eclipse.jgit.lib.Repository;
 
-import com.jcraft.jsch.JSchException;
-import com.jcraft.jsch.Session;
-
 /**
  * The base class for transports that use SSH protocol. This class allows
  * customizing SSH connection settings.
@@ -68,7 +62,7 @@ public abstract class SshTransport extends TcpTransport {
        /**
         * The open SSH session
         */
-       protected Session sock;
+       private RemoteSession sock;
 
        /**
         * Create a new transport instance.
@@ -111,35 +105,22 @@ public abstract class SshTransport extends TcpTransport {
                return sch;
        }
 
-
        /**
-        * Initialize SSH session
-        *
+        * Get the default SSH session
+        * 
+        * @return a remote session
         * @throws TransportException
         *             in case of error with opening SSH session
         */
-       protected void initSession() throws TransportException {
+       protected RemoteSession getSession() throws TransportException {
                if (sock != null)
-                       return;
+                       return sock;
 
                final int tms = getTimeout() > 0 ? getTimeout() * 1000 : 0;
-               final String user = uri.getUser();
-               final String pass = uri.getPass();
-               final String host = uri.getHost();
-               final int port = uri.getPort();
-               try {
-                       sock = sch.getSession(user, pass, host, port,
-                                       getCredentialsProvider(), local.getFS());
-                       if (!sock.isConnected())
-                               sock.connect(tms);
-               } catch (JSchException je) {
-                       final Throwable c = je.getCause();
-                       if (c instanceof UnknownHostException)
-                               throw new TransportException(uri, JGitText.get().unknownHost);
-                       if (c instanceof ConnectException)
-                               throw new TransportException(uri, c.getMessage());
-                       throw new TransportException(uri, je.getMessage(), je);
-               }
+
+               sock = sch
+                               .getSession(uri, getCredentialsProvider(), local.getFS(), tms);
+               return sock;
        }
 
        @Override
index 47959f5c9b0680dfc124bb62dddedca22e16b0ff..33a87109bf3834c8e27230010be810b5510128a8 100644 (file)
@@ -48,9 +48,6 @@ 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 java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -70,9 +67,7 @@ import org.eclipse.jgit.util.QuotedString;
 import org.eclipse.jgit.util.SystemReader;
 import org.eclipse.jgit.util.io.MessageWriter;
 import org.eclipse.jgit.util.io.StreamCopyThread;
-
-import com.jcraft.jsch.ChannelExec;
-import com.jcraft.jsch.JSchException;
+import org.eclipse.jgit.util.FS;
 
 /**
  * Transport through an SSH tunnel.
@@ -135,22 +130,26 @@ public class TransportGitSsh extends SshTransport implements PackTransport {
 
        TransportGitSsh(final Repository local, final URIish uri) {
                super(local, uri);
+               if (useExtSession()) {
+                       setSshSessionFactory(new SshSessionFactory() {
+                               @Override
+                               public RemoteSession getSession(URIish uri2,
+                                               CredentialsProvider credentialsProvider, FS fs, int tms)
+                                               throws TransportException {
+                                       return new ExtSession();
+                               }
+                       });
+               }
        }
 
        @Override
        public FetchConnection openFetch() throws TransportException {
-               return new SshFetchConnection(newConnection());
+               return new SshFetchConnection();
        }
 
        @Override
        public PushConnection openPush() throws TransportException {
-               return new SshPushConnection(newConnection());
-       }
-
-       private Connection newConnection() {
-               if (useExtConnection())
-                       return new ExtConnection();
-               return new JschConnection();
+               return new SshPushConnection();
        }
 
        String commandFor(final String exe) {
@@ -195,123 +194,13 @@ public class TransportGitSsh extends SshTransport implements PackTransport {
                return new NoRemoteRepositoryException(uri, why);
        }
 
-       private abstract class Connection {
-               abstract void exec(String commandName) throws TransportException;
-
-               abstract void connect() throws TransportException;
-
-               abstract InputStream getInputStream() throws IOException;
-
-               abstract OutputStream getOutputStream() throws IOException;
-
-               abstract InputStream getErrorStream() throws IOException;
-
-               abstract int getExitStatus();
-
-               abstract void close();
-       }
-
-       private class JschConnection extends Connection {
-               private ChannelExec channel;
-
-               private int exitStatus;
-
-               @Override
-               void exec(String commandName) throws TransportException {
-                       initSession();
-                       try {
-                               channel = (ChannelExec) sock.openChannel("exec");
-                               channel.setCommand(commandFor(commandName));
-                       } catch (JSchException je) {
-                               throw new TransportException(uri, je.getMessage(), je);
-                       }
-               }
-
-               @Override
-               void connect() throws TransportException {
-                       try {
-                               channel.connect(getTimeout() > 0 ? getTimeout() * 1000 : 0);
-                               if (!channel.isConnected())
-                                       throw new TransportException(uri, "connection failed");
-                       } catch (JSchException e) {
-                               throw new TransportException(uri, e.getMessage(), e);
-                       }
-               }
-
-               @Override
-               InputStream getInputStream() throws IOException {
-                       return channel.getInputStream();
-               }
-
-               @Override
-               OutputStream getOutputStream() throws IOException {
-                       // 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 (getTimeout() <= 0)
-                               return out;
-                       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(getTimeout() * 1000);
-                                       } catch (InterruptedException e) {
-                                               // Just wake early, the thread will terminate anyway.
-                                       }
-                               }
-                       };
-                       copier.start();
-                       return pipeOut;
-               }
-
-               @Override
-               InputStream getErrorStream() throws IOException {
-                       return channel.getErrStream();
-               }
-
-               @Override
-               int getExitStatus() {
-                       return exitStatus;
-               }
-
-               @Override
-               void close() {
-                       if (channel != null) {
-                               try {
-                                       exitStatus = channel.getExitStatus();
-                                       if (channel.isConnected())
-                                               channel.disconnect();
-                               } finally {
-                                       channel = null;
-                               }
-                       }
-               }
-       }
-
-       private static boolean useExtConnection() {
+       private static boolean useExtSession() {
                return SystemReader.getInstance().getenv("GIT_SSH") != null;
        }
 
-       private class ExtConnection extends Connection {
-               private Process proc;
-
-               private int exitStatus;
-
-               @Override
-               void exec(String commandName) throws TransportException {
+       private class ExtSession implements RemoteSession {
+               public Process exec(String command, int timeout)
+                               throws TransportException {
                        String ssh = SystemReader.getInstance().getenv("GIT_SSH");
                        boolean putty = ssh.toLowerCase().contains("plink");
 
@@ -327,7 +216,7 @@ public class TransportGitSsh extends SshTransport implements PackTransport {
                                args.add(getURI().getUser() + "@" + getURI().getHost());
                        else
                                args.add(getURI().getHost());
-                       args.add(commandFor(commandName));
+                       args.add(command);
 
                        ProcessBuilder pb = new ProcessBuilder();
                        pb.command(args);
@@ -337,73 +226,35 @@ public class TransportGitSsh extends SshTransport implements PackTransport {
                                                local.getDirectory().getPath());
 
                        try {
-                               proc = pb.start();
+                               return pb.start();
                        } catch (IOException err) {
-                               throw new TransportException(uri, err.getMessage(), err);
+                               throw new TransportException(err.getMessage(), err);
                        }
                }
 
-               @Override
-               void connect() throws TransportException {
-                       // Nothing to do, the process was already opened.
-               }
-
-               @Override
-               InputStream getInputStream() throws IOException {
-                       return proc.getInputStream();
-               }
-
-               @Override
-               OutputStream getOutputStream() throws IOException {
-                       return proc.getOutputStream();
-               }
-
-               @Override
-               InputStream getErrorStream() throws IOException {
-                       return proc.getErrorStream();
-               }
-
-               @Override
-               int getExitStatus() {
-                       return exitStatus;
-               }
-
-               @Override
-               void close() {
-                       if (proc != null) {
-                               try {
-                                       try {
-                                               exitStatus = proc.waitFor();
-                                       } catch (InterruptedException e) {
-                                               // Ignore the interrupt, but return immediately.
-                                       }
-                               } finally {
-                                       proc = null;
-                               }
-                       }
+               public void disconnect() {
+                       // Nothing to do
                }
        }
 
        class SshFetchConnection extends BasePackFetchConnection {
-               private Connection conn;
+               private final Process process;
 
                private StreamCopyThread errorThread;
 
-               SshFetchConnection(Connection conn) throws TransportException {
+               SshFetchConnection() throws TransportException {
                        super(TransportGitSsh.this);
-                       this.conn = conn;
                        try {
+                               process = getSession().exec(commandFor(getOptionUploadPack()),
+                                               getTimeout());
                                final MessageWriter msg = new MessageWriter();
                                setMessageWriter(msg);
 
-                               conn.exec(getOptionUploadPack());
-
-                               final InputStream upErr = conn.getErrorStream();
+                               final InputStream upErr = process.getErrorStream();
                                errorThread = new StreamCopyThread(upErr, msg.getRawStream());
                                errorThread.start();
 
-                               init(conn.getInputStream(), conn.getOutputStream());
-                               conn.connect();
+                               init(process.getInputStream(), process.getOutputStream());
 
                        } catch (TransportException err) {
                                close();
@@ -418,7 +269,7 @@ public class TransportGitSsh extends SshTransport implements PackTransport {
                                readAdvertisedRefs();
                        } catch (NoRemoteRepositoryException notFound) {
                                final String msgs = getMessages();
-                               checkExecFailure(conn.getExitStatus(), getOptionUploadPack(),
+                               checkExecFailure(process.exitValue(), getOptionUploadPack(),
                                                msgs);
                                throw cleanNotFound(notFound, msgs);
                        }
@@ -439,30 +290,28 @@ public class TransportGitSsh extends SshTransport implements PackTransport {
                        }
 
                        super.close();
-                       conn.close();
+                       process.destroy();
                }
        }
 
        class SshPushConnection extends BasePackPushConnection {
-               private Connection conn;
+               private final Process process;
 
                private StreamCopyThread errorThread;
 
-               SshPushConnection(Connection conn) throws TransportException {
+               SshPushConnection() throws TransportException {
                        super(TransportGitSsh.this);
-                       this.conn = conn;
                        try {
+                               process = getSession().exec(commandFor(getOptionReceivePack()),
+                                               getTimeout());
                                final MessageWriter msg = new MessageWriter();
                                setMessageWriter(msg);
 
-                               conn.exec(getOptionReceivePack());
-
-                               final InputStream rpErr = conn.getErrorStream();
+                               final InputStream rpErr = process.getErrorStream();
                                errorThread = new StreamCopyThread(rpErr, msg.getRawStream());
                                errorThread.start();
 
-                               init(conn.getInputStream(), conn.getOutputStream());
-                               conn.connect();
+                               init(process.getInputStream(), process.getOutputStream());
 
                        } catch (TransportException err) {
                                close();
@@ -477,7 +326,7 @@ public class TransportGitSsh extends SshTransport implements PackTransport {
                                readAdvertisedRefs();
                        } catch (NoRemoteRepositoryException notFound) {
                                final String msgs = getMessages();
-                               checkExecFailure(conn.getExitStatus(), getOptionReceivePack(),
+                               checkExecFailure(process.exitValue(), getOptionReceivePack(),
                                                msgs);
                                throw cleanNotFound(notFound, msgs);
                        }
@@ -498,7 +347,7 @@ public class TransportGitSsh extends SshTransport implements PackTransport {
                        }
 
                        super.close();
-                       conn.close();
+                       process.destroy();
                }
        }
 }
index 9ab4a99264830959049e1392ab6baa2630a210c5..2388b543c093e0c0a33eaff00fae80740cb05c4e 100644 (file)
@@ -147,11 +147,12 @@ public class TransportSftp extends SshTransport implements WalkTransport {
        }
 
        ChannelSftp newSftp() throws TransportException {
-               initSession();
-
                final int tms = getTimeout() > 0 ? getTimeout() * 1000 : 0;
                try {
-                       final Channel channel = sock.openChannel("sftp");
+                       // @TODO: Fix so that this operation is generic and casting to
+                       // JschSession is no longer necessary.
+                       final Channel channel = ((JschSession) getSession())
+                                       .getSftpChannel();
                        channel.connect(tms);
                        return (ChannelSftp) channel;
                } catch (JSchException je) {