You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ha...@apache.org on 2012/02/28 04:18:33 UTC

svn commit: r1294451 - in /camel/trunk/components/camel-jsch/src: main/java/org/apache/camel/component/jsch/ test/java/org/apache/camel/component/jsch/ test/resources/

Author: hadrian
Date: Tue Feb 28 03:18:33 2012
New Revision: 1294451

URL: http://svn.apache.org/viewvc?rev=1294451&view=rev
Log:
CAMEL-5020. Support for recursive copy

Removed:
    camel/trunk/components/camel-jsch/src/test/resources/client.jks
    camel/trunk/components/camel-jsch/src/test/resources/server.jks
Modified:
    camel/trunk/components/camel-jsch/src/main/java/org/apache/camel/component/jsch/ScpOperations.java
    camel/trunk/components/camel-jsch/src/test/java/org/apache/camel/component/jsch/ScpServerTestSupport.java
    camel/trunk/components/camel-jsch/src/test/java/org/apache/camel/component/jsch/ScpSimpleProduceTest.java

Modified: camel/trunk/components/camel-jsch/src/main/java/org/apache/camel/component/jsch/ScpOperations.java
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-jsch/src/main/java/org/apache/camel/component/jsch/ScpOperations.java?rev=1294451&r1=1294450&r2=1294451&view=diff
==============================================================================
--- camel/trunk/components/camel-jsch/src/main/java/org/apache/camel/component/jsch/ScpOperations.java (original)
+++ camel/trunk/components/camel-jsch/src/main/java/org/apache/camel/component/jsch/ScpOperations.java Tue Feb 28 03:18:33 2012
@@ -16,6 +16,7 @@
  */
 package org.apache.camel.component.jsch;
 
+import java.io.BufferedInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -30,10 +31,12 @@ import com.jcraft.jsch.UIKeyboardInterac
 import com.jcraft.jsch.UserInfo;
 
 import org.apache.camel.Exchange;
+import org.apache.camel.InvalidPayloadException;
 import org.apache.camel.component.file.GenericFileEndpoint;
 import org.apache.camel.component.file.GenericFileOperationFailedException;
 import org.apache.camel.component.file.remote.RemoteFileConfiguration;
 import org.apache.camel.component.file.remote.RemoteFileOperations;
+import org.apache.camel.util.ExchangeHelper;
 import org.apache.camel.util.ObjectHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -92,20 +95,17 @@ public class ScpOperations implements Re
         int timeout = cfg.getConnectTimeout();
         LOG.trace("Opening channel to {} with {} timeout...", cfg.remoteServerInformation(), 
             timeout > 0 ? (Integer.toString(timeout) + " ms") : "no");
-        String target = getRemoteTarget(cfg);
         String file = getRemoteFile(name, cfg);
         try {
             channel = (ChannelExec) session.openChannel("exec");
-            // TODO: need config for scp *-p* (preserves modification times, access times, and modes from the original file)
-            // String command="scp " + (ptimestamp ? "-p " : "") + "-t " + configuration.getDirectory();
-            // TODO: refactor to use generic command
-            String command = "scp -t " + target;
-            channel.setCommand(command);
+            channel.setCommand(getScpCommand(cfg, file));
             channel.connect(timeout);
             LOG.trace("Channel connected to {}", cfg.remoteServerInformation());
 
             try {
-                writeFile(channel, file, "foo");
+                write(channel, file, ExchangeHelper.getMandatoryInBody(exchange, InputStream.class));
+            } catch (InvalidPayloadException e) {
+                throw new GenericFileOperationFailedException("Failed extract message body as InputStream", e);
             } catch (IOException e) {
                 throw new GenericFileOperationFailedException("Failed to write file " + file, e);
             }
@@ -198,10 +198,7 @@ public class ScpOperations implements Re
                 ciphers.put("cipher.c2s", config.getCiphers());
                 JSch.setConfig(ciphers);
             }
-            
 
-            
-            
             String knownHostsFile = config.getKnownHostsFile();
             jsch.setKnownHosts(ObjectHelper.isEmpty(knownHostsFile) ? DEFAULT_KNOWN_HOSTS : knownHostsFile);
             session = jsch.getSession(config.getUsername(), config.getHost(), config.getPort());
@@ -223,24 +220,59 @@ public class ScpOperations implements Re
         return session;
     }
     
-    private void writeFile(ChannelExec c, String name, String data) throws IOException {
-        data = "Hello World";
+    private void write(ChannelExec c, String name, InputStream data) throws IOException {
         OutputStream os = c.getOutputStream();
         InputStream is = c.getInputStream();
 
-        os.write(("C7777 " + data.length() + " " + name + "\n").getBytes());
-        os.flush();
-        is.read();
-        
-        os.write(data.getBytes());
-        os.flush();
-        is.read();
-        
-        os.write(0);
-        os.flush();
+        writeFile(name, data, os, is);
+
         os.close();
         is.close();
     }
+
+    private void writeFile(String filename, InputStream data, OutputStream os, InputStream is) throws IOException {
+        int pos = filename.indexOf('/');
+        if (pos >= 0) {
+            // write to child directory
+            String dir = filename.substring(0, pos);
+            os.write(("D0775 0 " + dir + "\n").getBytes());
+            os.flush();
+            is.read();
+
+            writeFile(filename.substring(pos + 1), data, os, is);
+
+            os.write(("E\n").getBytes());
+            os.flush();
+            is.read();
+        } else {
+            int count = 0;
+            int read = 0;
+            int size = endpoint.getBufferSize();
+            byte[] bytes = new byte[size];
+
+            // figure out the stream size as we need to pass it in the header
+            BufferedInputStream buffer = new BufferedInputStream(data, size);
+            buffer.mark(Integer.MAX_VALUE);
+            while ((read = buffer.read(bytes)) != -1) {
+                count += read;
+            }
+
+            // send the header
+            os.write(("C0775 " + count + " " + filename + "\n").getBytes());
+            os.flush();
+            is.read();
+
+            // now send the stream
+            buffer.reset();
+            while ((read = buffer.read(bytes)) != -1) {
+                os.write(bytes, 0, read);
+            }
+            os.flush();
+            is.read();
+        }
+        os.write(0);
+        os.flush();
+    }
     
     private static String getRemoteTarget(ScpConfiguration config) {
         // use current dir (".") if target directory not specified in uri
@@ -248,12 +280,27 @@ public class ScpOperations implements Re
     }
 
     private static String getRemoteFile(String name, ScpConfiguration config) {
-        // assume that the directory path of 'name' is the same as config.getDirectory()
-        int pos = name.lastIndexOf('/');
-        return pos >= 0 ? name.substring(pos + 1) : name;
+        String dir = config.getDirectory();
+        dir = dir.endsWith("/") ? dir : dir + "/";
+        return name.startsWith(dir) ? name.substring(dir.length()) : name;
+    }
+
+    private static boolean isRecursiveScp(String name) {
+        return name.indexOf('/') > 0;
+    }
+
+    private static String getScpCommand(ScpConfiguration config, String name) {
+        StringBuilder cmd = new StringBuilder();
+        cmd.append("scp ");
+        // TODO: need config for scp *-p* (preserves modification times, access times, and modes from the original file)
+        // String command="scp " + (ptimestamp ? "-p " : "") + "-t " + configuration.getDirectory();
+        // TODO: refactor to use generic command
+        cmd.append(isRecursiveScp(name) ? "-r " : "");
+        cmd.append("-t ");
+        cmd.append(getRemoteTarget(config));
+        return cmd.toString(); 
     }
 
-
     protected static final class SessionUserInfo implements UserInfo, UIKeyboardInteractive {
         private final ScpConfiguration config;
         public SessionUserInfo(ScpConfiguration config) {

Modified: camel/trunk/components/camel-jsch/src/test/java/org/apache/camel/component/jsch/ScpServerTestSupport.java
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-jsch/src/test/java/org/apache/camel/component/jsch/ScpServerTestSupport.java?rev=1294451&r1=1294450&r2=1294451&view=diff
==============================================================================
--- camel/trunk/components/camel-jsch/src/test/java/org/apache/camel/component/jsch/ScpServerTestSupport.java (original)
+++ camel/trunk/components/camel-jsch/src/test/java/org/apache/camel/component/jsch/ScpServerTestSupport.java Tue Feb 28 03:18:33 2012
@@ -80,8 +80,8 @@ public abstract class ScpServerTestSuppo
     @Override
     @Before
     public void setUp() throws Exception {
-        File root = new File(SCP_ROOT_DIR);
-        root.mkdirs();
+        File root = new File(getScpPath());
+        root.mkdirs(); // implicitly create sshd home dir
 
         setupComplete = startSshd();
         setupKnownHosts();
@@ -105,6 +105,17 @@ public abstract class ScpServerTestSuppo
         FileUtil.deleteFile(new File(knownHostsFile));
     }
 
+    protected final String getScpPath() {
+        // use this convention and use separate directories for tests
+        // (easier to debug and avoid interference)
+        return SCP_ROOT_DIR + "/" + getClass().getSimpleName();
+    }
+
+    protected String getScpUri() {
+        return "scp://localhost:" + getPort() + "/" + getScpPath();
+    }
+
+
     protected boolean startSshd() {
         sshd = SshServer.setUpDefaultServer();
         sshd.setPort(getPort());
@@ -139,8 +150,8 @@ public abstract class ScpServerTestSuppo
         // we use a known key, but since the port it dynamic, the 
         // known_hosts file will be generated by the following code and 
         // should should contain a line like below (if 
-        // "HashKnownHosts"=="yes" the hostname:port part will look
-        // a bit more complicated).
+        // "HashKnownHosts"=="yes" the hostname:port part will be 
+        // hashed and look a bit more complicated).
         //
         // [localhost]:21000 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDd \
         // fIWeSV4o68dRrKSzFd/Bk51E65UTmmSrmW0O1ohtzi6HzsDPjXgCtlTt3F \

Modified: camel/trunk/components/camel-jsch/src/test/java/org/apache/camel/component/jsch/ScpSimpleProduceTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-jsch/src/test/java/org/apache/camel/component/jsch/ScpSimpleProduceTest.java?rev=1294451&r1=1294450&r2=1294451&view=diff
==============================================================================
--- camel/trunk/components/camel-jsch/src/test/java/org/apache/camel/component/jsch/ScpSimpleProduceTest.java (original)
+++ camel/trunk/components/camel-jsch/src/test/java/org/apache/camel/component/jsch/ScpSimpleProduceTest.java Tue Feb 28 03:18:33 2012
@@ -27,7 +27,6 @@ import org.junit.Test;
  * @version 
  */
 public class ScpSimpleProduceTest extends ScpServerTestSupport {
-
     @Override
     public boolean isUseRouteBuilder() {
         return false;
@@ -37,36 +36,34 @@ public class ScpSimpleProduceTest extend
     public void testScpSimpleProduce() throws Exception {
         Assume.assumeTrue(this.isSetupComplete());
 
-        String uri = "scp://localhost:" + getPort() + "/target/scp?username=admin&password=admin&knownHostsFile=" + getKnownHostsFile();
+        String uri = getScpUri() + "?username=admin&password=admin&knownHostsFile=" + getKnownHostsFile();
         template.sendBodyAndHeader(uri, "Hello World", Exchange.FILE_NAME, "hello.txt");
 
-        File file = new File(SCP_ROOT_DIR + "/hello.txt").getAbsoluteFile();
+        File file = new File(getScpPath() + "/hello.txt").getAbsoluteFile();
         assertTrue("File should exist: " + file, file.exists());
         assertEquals("Hello World", context.getTypeConverter().convertTo(String.class, file));
     }
 
     @Test
-    @Ignore("Scenario not supported by scp but could be emulated with recursive copy")
     public void testScpSimpleSubPathProduce() throws Exception {
         Assume.assumeTrue(this.isSetupComplete());
 
-        String uri = "scp://localhost:" + getPort() + "/target/scp?username=admin&password=admin&knownHostsFile=" + getKnownHostsFile();
-        template.sendBodyAndHeader(uri, "Bye World", Exchange.FILE_NAME, "bye.txt");
+        String uri = getScpUri() + "?username=admin&password=admin&knownHostsFile=" + getKnownHostsFile();
+        template.sendBodyAndHeader(uri, "Bye World", Exchange.FILE_NAME, "mysub/bye.txt");
 
-        File file = new File(SCP_ROOT_DIR + "/mysub/bye.txt").getAbsoluteFile();
+        File file = new File(getScpPath() + "/mysub/bye.txt").getAbsoluteFile();
         assertTrue("File should exist: " + file, file.exists());
         assertEquals("Bye World", context.getTypeConverter().convertTo(String.class, file));
     }
 
     @Test
-    @Ignore("Scenario not supported by scp but could be emulated with recursive copy")
     public void testScpSimpleTwoSubPathProduce() throws Exception {
         Assume.assumeTrue(this.isSetupComplete());
 
-        String uri = "scp://localhost:" + getPort() + "/target/scp?username=admin&password=admin&knownHostsFile=" + getKnownHostsFile();
-        template.sendBodyAndHeader(uri, "Farewell World", Exchange.FILE_NAME, "farewell.txt");
+        String uri = getScpUri() + "?username=admin&password=admin&knownHostsFile=" + getKnownHostsFile();
+        template.sendBodyAndHeader(uri, "Farewell World", Exchange.FILE_NAME, "mysub/mysubsub/farewell.txt");
 
-        File file = new File(SCP_ROOT_DIR + "/mysub/myother/farewell.txt").getAbsoluteFile();
+        File file = new File(getScpPath() + "/mysub/mysubsub/farewell.txt").getAbsoluteFile();
         assertTrue("File should exist: " + file, file.exists());
         assertEquals("Farewell World", context.getTypeConverter().convertTo(String.class, file));
     }