Author: gnodet
Date: Mon Dec 22 03:49:36 2008
New Revision: 728655

URL: http://svn.apache.org/viewvc?rev=728655&view=rev
Log:
SSHD-4: Remove support for InvertedShell and provide a wrapper instead

Added:
    
mina/sshd/trunk/src/main/java/org/apache/sshd/server/shell/InvertedShell.java   
(with props)
    
mina/sshd/trunk/src/main/java/org/apache/sshd/server/shell/InvertedShellWrapper.java
   (with props)
Modified:
    mina/sshd/trunk/src/main/java/org/apache/sshd/server/ShellFactory.java
    
mina/sshd/trunk/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
    
mina/sshd/trunk/src/main/java/org/apache/sshd/server/shell/ProcessShellFactory.java
    mina/sshd/trunk/src/test/java/org/apache/sshd/util/EchoShellFactory.java

Modified: mina/sshd/trunk/src/main/java/org/apache/sshd/server/ShellFactory.java
URL: 
http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/ShellFactory.java?rev=728655&r1=728654&r2=728655&view=diff
==============================================================================
--- mina/sshd/trunk/src/main/java/org/apache/sshd/server/ShellFactory.java 
(original)
+++ mina/sshd/trunk/src/main/java/org/apache/sshd/server/ShellFactory.java Mon 
Dec 22 03:49:36 2008
@@ -25,7 +25,6 @@
 
 /**
  *
- * TODO: remove InvertedShell and create a wrapper instead to keep the api 
cleaner
  * TODO Add javadoc
  *
  * @author <a href="mailto:[email protected]";>Apache MINA SSHD Project</a>
@@ -43,78 +42,12 @@
 
     /**
      * Represents a shell that can be used in an interactive mode to send 
command.
-     * Shell implementations must implement one of the two sub-interfaces 
{...@link InvertedShell}
-     * or {...@link DirectShell} which have different ways to configure the 
input and output streams.
-     */
-    public interface Shell {
-
-        /**
-         * Destroy the shell.
-         * This method can be called by the SSH server to destroy the shell 
because
-         * the client has disconnected somehow.
-         */
-        void destroy();
-
-    }
-
-    /**
-     * This shell have inverted streams, such as the one obtained when 
launching a
-     * new {...@link Process} from java.
-     */
-    public interface InvertedShell extends Shell {
-
-        /**
-         * Starts the shell and will make the streams available for
-         * the ssh server to retrieve and use.
-         *
-         * @param env
-         * @throws Exception
-         */
-        void start(Map<String,String> env) throws IOException;
-
-        /**
-         * Returns the output stream used to feed the shell.
-         * This method is called after the shell has been started.
-         *
-         * @return
-         */
-        OutputStream getInputStream();
-
-        /**
-         * Return an InputStream representing the output stream of the shell.
-         * @return
-         */
-        InputStream getOutputStream();
-
-        /**
-         * Return an InputStream representing the error stream of the shell.
-         * @return
-         */
-        InputStream getErrorStream();
-
-        /**
-         * Check if the underlying shell is still alive
-         * @return
-         */
-        boolean isAlive();
-
-        /**
-         * Retrieve the exit value of the shell.
-         * This method must only be called when the shell is not alive anymore.
-         *
-         * @return the exit value of the shell
-         */
-        int exitValue();
-
-    }
-
-    /**
      * This shell have direct streams, meaning those streams will be provided 
by the ssh server
-     * for the shell to use directy.
-     * This interface is suitable for implementing shells in java, rather than 
using an external
-     * process.
+     * for the shell to use directy. This interface is suitable for 
implementing shells in java,
+     * rather than using an external process.  For wrapping such processes or 
using inverted streams,
+     * see {...@link org.apache.sshd.server.shell.InvertedShellWrapper}.
      */
-    public interface DirectShell extends Shell {
+    public interface Shell {
 
         /**
          * Set the input stream that can be used by the shell to read input.
@@ -148,6 +81,13 @@
          */
         void start(Map<String,String> env) throws IOException;
 
+        /**
+         * Destroy the shell.
+         * This method can be called by the SSH server to destroy the shell 
because
+         * the client has disconnected somehow.
+         */
+        void destroy();
+
     }
 
     /**

Modified: 
mina/sshd/trunk/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
URL: 
http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/channel/ChannelSession.java?rev=728655&r1=728654&r2=728655&view=diff
==============================================================================
--- 
mina/sshd/trunk/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
 (original)
+++ 
mina/sshd/trunk/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
 Mon Dec 22 03:49:36 2008
@@ -126,11 +126,8 @@
     protected InputStream shellOut;
     protected InputStream shellErr;
     protected Map<String,String> env;
-    protected ExecutorService executor;
-    protected boolean usePipedChannelStreams;
 
     public ChannelSession() {
-        executor = Executors.newSingleThreadExecutor();
     }
 
     public CloseFuture close(boolean immediately) {
@@ -141,7 +138,6 @@
                     shell = null;
                 }
                 IoUtils.closeQuietly(in, out, err, shellIn, shellOut, 
shellErr);
-                executor.shutdown();
             }
         });
     }
@@ -158,26 +154,8 @@
     }
 
     protected void doWriteData(byte[] data, int off, int len) throws 
IOException {
-        if (usePipedChannelStreams) {
-            shellIn.write(data, off, len);
-            shellIn.flush();
-        } else {
-            final byte[] buf = new byte[len];
-            System.arraycopy(data, off, buf, 0, len);
-            executor.execute(new Runnable() {
-                public void run() {
-                    try {
-                        if (shellIn != null) {
-                            shellIn.write(buf);
-                            shellIn.flush();
-                        }
-                    } catch (IOException e) {
-                        close(false);
-                    }
-                }
-            });
-            localWindow.consumeAndCheck(len);
-        }
+        shellIn.write(data, off, len);
+        shellIn.flush();
     }
 
     protected void doWriteExtendedData(byte[] data, int off, int len) throws 
IOException {
@@ -336,38 +314,21 @@
             out = new LfToCrLfFilterOutputStream(out);
             err = new LfToCrLfFilterOutputStream(err);
         }
-        if (shell instanceof ShellFactory.InvertedShell) {
-            ((ShellFactory.InvertedShell) shell).start(env);
-            shellIn = ((ShellFactory.InvertedShell) shell).getInputStream();
-            shellOut = ((ShellFactory.InvertedShell) shell).getOutputStream();
-            shellErr = ((ShellFactory.InvertedShell) shell).getErrorStream();
-            new Thread() {
-                @Override
-                public void run() {
-                    pumpStreams();
-                }
-            }.start();
-            // TODO: set this thread as daemon?
-        } else if (shell instanceof ShellFactory.DirectShell) {
-            in = new ChannelPipedInputStream(localWindow);
-            shellIn = new ChannelPipedOutputStream((ChannelPipedInputStream) 
in);
-            usePipedChannelStreams = true;
-            ((ShellFactory.DirectShell) shell).setInputStream(in);
-            ((ShellFactory.DirectShell) shell).setOutputStream(out);
-            ((ShellFactory.DirectShell) shell).setErrorStream(err);
-            ((ShellFactory.DirectShell) shell).setExitCallback(new 
ShellFactory.ExitCallback() {
-                public void onExit(int exitValue) {
-                    try {
-                        closeShell(exitValue);
-                    } catch (IOException e) {
-                        log.info("Error closing shell", e);
-                    }
+        in = new ChannelPipedInputStream(localWindow);
+        shellIn = new ChannelPipedOutputStream((ChannelPipedInputStream) in);
+        shell.setInputStream(in);
+        shell.setOutputStream(out);
+        shell.setErrorStream(err);
+        shell.setExitCallback(new ShellFactory.ExitCallback() {
+            public void onExit(int exitValue) {
+                try {
+                    closeShell(exitValue);
+                } catch (IOException e) {
+                    log.info("Error closing shell", e);
                 }
-            });
-            ((ShellFactory.DirectShell) shell).start(env);
-        } else {
-            throw new IllegalStateException("Unknown shell type");
-        }
+            }
+        });
+        shell.start(env);
         shellIn = new LoggingFilterOutputStream(shellIn, "IN: ", log);
 
         if (wantReply) {
@@ -410,7 +371,6 @@
         err = new LoggingFilterOutputStream(err, "ERR:", log);
         in = new ChannelPipedInputStream(localWindow);
         shellIn = new ChannelPipedOutputStream((ChannelPipedInputStream) in);
-        usePipedChannelStreams = true;
         command.setInputStream(in);
         command.setOutputStream(out);
         command.setErrorStream(err);
@@ -453,46 +413,6 @@
         env.put(name, value);
     }
 
-    protected void pumpStreams() {
-        try {
-            // Use a single thread to correctly sequence the output and error 
streams.
-            // If any bytes are available from the output stream, send them 
first, then
-            // check the error stream, or wait until more data is available.
-            ShellFactory.InvertedShell is = (ShellFactory.InvertedShell) shell;
-            byte[] buffer = new byte[512];
-            for (;;) {
-                if (!is.isAlive()) {
-                    closeShell(is.exitValue());
-                    return;
-                }
-                if (pumpStream(shellOut, out, buffer)) {
-                    continue;
-                }
-                if (pumpStream(shellErr, err, buffer)) {
-                    continue;
-                }
-                // Sleep a bit.  This is not very good, as it consumes CPU, 
but the
-                // input streams are not selectable for nio, and any other 
blocking
-                // method would consume at least two threads
-                Thread.sleep(1);
-            }
-        } catch (Exception e) {
-            session.close(false);
-        }
-    }
-
-    private boolean pumpStream(InputStream in, OutputStream out, byte[] 
buffer) throws IOException {
-        if (in.available() > 0) {
-            int len = in.read(buffer);
-            if (len > 0) {
-                out.write(buffer, 0, len);
-                out.flush();
-                return true;
-            }
-        }
-        return false;
-    }
-
     protected void closeShell(int exitValue) throws IOException {
         sendEof();
         sendExitStatus(exitValue);

Added: 
mina/sshd/trunk/src/main/java/org/apache/sshd/server/shell/InvertedShell.java
URL: 
http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/shell/InvertedShell.java?rev=728655&view=auto
==============================================================================
--- 
mina/sshd/trunk/src/main/java/org/apache/sshd/server/shell/InvertedShell.java 
(added)
+++ 
mina/sshd/trunk/src/main/java/org/apache/sshd/server/shell/InvertedShell.java 
Mon Dec 22 03:49:36 2008
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.server.shell;
+
+import java.util.Map;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.InputStream;
+
+/**
+ * This shell have inverted streams, such as the one obtained when launching a
+ * new {...@link Process} from java.  This interface is meant to be used with
+ * {...@link InvertedShellWrapper} class as an implementation of
+ * {...@link org.apache.sshd.server.ShellFactory.Shell}.
+ *
+ * @author <a href="mailto:[email protected]";>Apache MINA SSHD Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface InvertedShell {
+
+    /**
+     * Starts the shell and will make the streams available for
+     * the ssh server to retrieve and use.
+     *
+     * @param env
+     * @throws Exception
+     */
+    void start(Map<String,String> env) throws IOException;
+
+    /**
+     * Returns the output stream used to feed the shell.
+     * This method is called after the shell has been started.
+     *
+     * @return
+     */
+    OutputStream getInputStream();
+
+    /**
+     * Return an InputStream representing the output stream of the shell.
+     * @return
+     */
+    InputStream getOutputStream();
+
+    /**
+     * Return an InputStream representing the error stream of the shell.
+     * @return
+     */
+    InputStream getErrorStream();
+
+    /**
+     * Check if the underlying shell is still alive
+     * @return
+     */
+    boolean isAlive();
+
+    /**
+     * Retrieve the exit value of the shell.
+     * This method must only be called when the shell is not alive anymore.
+     *
+     * @return the exit value of the shell
+     */
+    int exitValue();
+
+    /**
+     * Destroy the shell.
+     * This method can be called by the SSH server to destroy the shell because
+     * the client has disconnected somehow.
+     */
+    void destroy();
+}

Propchange: 
mina/sshd/trunk/src/main/java/org/apache/sshd/server/shell/InvertedShell.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
mina/sshd/trunk/src/main/java/org/apache/sshd/server/shell/InvertedShell.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: 
mina/sshd/trunk/src/main/java/org/apache/sshd/server/shell/InvertedShellWrapper.java
URL: 
http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/shell/InvertedShellWrapper.java?rev=728655&view=auto
==============================================================================
--- 
mina/sshd/trunk/src/main/java/org/apache/sshd/server/shell/InvertedShellWrapper.java
 (added)
+++ 
mina/sshd/trunk/src/main/java/org/apache/sshd/server/shell/InvertedShellWrapper.java
 Mon Dec 22 03:49:36 2008
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.server.shell;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.sshd.server.ShellFactory;
+
+/**
+ * A shell implementation that wraps an instance of {...@link InvertedShell}
+ * as a {...@link ShellFactory.Shell}.  This is useful when using external
+ * processes.
+ * When starting the shell, this wrapper will also create a thread used
+ * to pump the streams and also to check if the shell is alive. 
+ *
+ * @author <a href="mailto:[email protected]";>Apache MINA SSHD Project</a>
+ * @version $Rev$, $Date$
+ */
+public class InvertedShellWrapper implements ShellFactory.Shell {
+
+    private final InvertedShell shell;
+    private InputStream in;
+    private OutputStream out;
+    private OutputStream err;
+    private OutputStream shellIn;
+    private InputStream shellOut;
+    private InputStream shellErr;
+    private ShellFactory.ExitCallback callback;
+    private Thread thread;
+
+    public InvertedShellWrapper(InvertedShell shell) {
+        this.shell = shell;
+    }
+
+    public void setInputStream(InputStream in) {
+        this.in = in;
+    }
+
+    public void setOutputStream(OutputStream out) {
+        this.out = out;
+    }
+
+    public void setErrorStream(OutputStream err) {
+        this.err = err;
+    }
+
+    public void setExitCallback(ShellFactory.ExitCallback callback) {
+        this.callback = callback;
+    }
+
+    public void start(Map<String, String> env) throws IOException {
+        shell.start(env);
+        shellIn = shell.getInputStream();
+        shellOut = shell.getOutputStream();
+        shellErr = shell.getErrorStream();
+        thread = new Thread("inverted-shell-pump") {
+            @Override
+            public void run() {
+                pumpStreams();
+            }
+        };
+        thread.start();
+    }
+
+    public void destroy() {
+        shell.destroy();
+    }
+
+    protected void pumpStreams() {
+        try {
+            // Use a single thread to correctly sequence the output and error 
streams.
+            // If any bytes are available from the output stream, send them 
first, then
+            // check the error stream, or wait until more data is available.
+            byte[] buffer = new byte[512];
+            for (;;) {
+                if (!shell.isAlive()) {
+                    callback.onExit(shell.exitValue());
+                    return;
+                }
+                if (pumpStream(in, shellIn, buffer)) {
+                    continue;
+                }
+                if (pumpStream(shellOut, out, buffer)) {
+                    continue;
+                }
+                if (pumpStream(shellErr, err, buffer)) {
+                    continue;
+                }
+                // Sleep a bit.  This is not very good, as it consumes CPU, 
but the
+                // input streams are not selectable for nio, and any other 
blocking
+                // method would consume at least two threads
+                Thread.sleep(1);
+            }
+        } catch (Exception e) {
+            shell.destroy();
+            callback.onExit(shell.exitValue());
+        }
+    }
+
+    private boolean pumpStream(InputStream in, OutputStream out, byte[] 
buffer) throws IOException {
+        if (in.available() > 0) {
+            int len = in.read(buffer);
+            if (len > 0) {
+                out.write(buffer, 0, len);
+                out.flush();
+                return true;
+            }
+        }
+        return false;
+    }
+
+}

Propchange: 
mina/sshd/trunk/src/main/java/org/apache/sshd/server/shell/InvertedShellWrapper.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
mina/sshd/trunk/src/main/java/org/apache/sshd/server/shell/InvertedShellWrapper.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Modified: 
mina/sshd/trunk/src/main/java/org/apache/sshd/server/shell/ProcessShellFactory.java
URL: 
http://svn.apache.org/viewvc/mina/sshd/trunk/src/main/java/org/apache/sshd/server/shell/ProcessShellFactory.java?rev=728655&r1=728654&r2=728655&view=diff
==============================================================================
--- 
mina/sshd/trunk/src/main/java/org/apache/sshd/server/shell/ProcessShellFactory.java
 (original)
+++ 
mina/sshd/trunk/src/main/java/org/apache/sshd/server/shell/ProcessShellFactory.java
 Mon Dec 22 03:49:36 2008
@@ -28,7 +28,8 @@
 import org.slf4j.LoggerFactory;
 
 /**
- * TODO Add javadoc
+ * A {...@link ShellFactory} that will create a new process and bridge
+ * the streams.
  *
  * @author <a href="mailto:[email protected]";>Apache MINA SSHD Project</a>
  * @version $Rev$, $Date$
@@ -55,7 +56,7 @@
     }
 
     public Shell createShell() {
-        return new ProcessShell(command);
+        return new InvertedShellWrapper(new ProcessShell(command));
     }
 
     public static class ProcessShell implements InvertedShell {

Modified: 
mina/sshd/trunk/src/test/java/org/apache/sshd/util/EchoShellFactory.java
URL: 
http://svn.apache.org/viewvc/mina/sshd/trunk/src/test/java/org/apache/sshd/util/EchoShellFactory.java?rev=728655&r1=728654&r2=728655&view=diff
==============================================================================
--- mina/sshd/trunk/src/test/java/org/apache/sshd/util/EchoShellFactory.java 
(original)
+++ mina/sshd/trunk/src/test/java/org/apache/sshd/util/EchoShellFactory.java 
Mon Dec 22 03:49:36 2008
@@ -39,7 +39,7 @@
         return new EchoShell();
     }
 
-    protected static class EchoShell implements DirectShell, Runnable {
+    protected static class EchoShell implements Shell, Runnable {
         private InputStream in;
         private OutputStream out;
         private OutputStream err;


Reply via email to