CAMEL-8241: Work around bug in commons-exec to get camel-exec to work on unix 
with java 8.


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/bbbb3942
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/bbbb3942
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/bbbb3942

Branch: refs/heads/camel-2.14.x
Commit: bbbb3942e6ff42b5c0018e0770f3f55e207b527c
Parents: 0231cdc
Author: Claus Ibsen <davscl...@apache.org>
Authored: Sun Feb 22 11:51:21 2015 +0100
Committer: Claus Ibsen <davscl...@apache.org>
Committed: Sun Feb 22 11:55:24 2015 +0100

----------------------------------------------------------------------
 .../component/exec/ExecDefaultExecutor.java     |  45 +++++++
 .../camel/component/exec/ExecEndpoint.java      |   1 -
 .../camel/component/exec/ExecProducer.java      |   9 +-
 .../exec/impl/DefaultExecCommandExecutor.java   |  29 ++++-
 .../camel/component/exec/ExecEndpointTest.java  |   3 -
 .../component/exec/ExecJava8IssueTest.java      | 119 +++++++++++++++++++
 6 files changed, 200 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/bbbb3942/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecDefaultExecutor.java
----------------------------------------------------------------------
diff --git 
a/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecDefaultExecutor.java
 
b/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecDefaultExecutor.java
new file mode 100644
index 0000000..14df04e
--- /dev/null
+++ 
b/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecDefaultExecutor.java
@@ -0,0 +1,45 @@
+/**
+ * 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.camel.component.exec;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.commons.exec.CommandLine;
+import org.apache.commons.exec.DefaultExecutor;
+
+public class ExecDefaultExecutor extends DefaultExecutor {
+
+    private transient Process process;
+
+    public ExecDefaultExecutor() {
+    }
+
+    @Override
+    protected Process launch(CommandLine command, Map<String, String> env, 
File dir) throws IOException {
+        process = super.launch(command, env, dir);
+        return process;
+    }
+
+    public int getExitValue() {
+        if (process != null && !process.isAlive())  {
+            return process.exitValue();
+        }
+        return 0;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/bbbb3942/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecEndpoint.java
----------------------------------------------------------------------
diff --git 
a/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecEndpoint.java
 
b/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecEndpoint.java
index 8851328..202ac70 100644
--- 
a/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecEndpoint.java
+++ 
b/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecEndpoint.java
@@ -61,7 +61,6 @@ public class ExecEndpoint extends DefaultEndpoint {
         super(uri, component);
         this.timeout = NO_TIMEOUT;
         this.binding = new DefaultExecBinding();
-        this.commandExecutor = new DefaultExecCommandExecutor();
     }
 
     public Producer createProducer() throws Exception {

http://git-wip-us.apache.org/repos/asf/camel/blob/bbbb3942/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecProducer.java
----------------------------------------------------------------------
diff --git 
a/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecProducer.java
 
b/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecProducer.java
index aa125d4..49e1457 100644
--- 
a/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecProducer.java
+++ 
b/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecProducer.java
@@ -17,6 +17,7 @@
 package org.apache.camel.component.exec;
 
 import org.apache.camel.Exchange;
+import org.apache.camel.component.exec.impl.DefaultExecCommandExecutor;
 import org.apache.camel.impl.DefaultProducer;
 import org.apache.camel.util.ObjectHelper;
 import org.slf4j.Logger;
@@ -42,8 +43,14 @@ public class ExecProducer extends DefaultProducer {
     public void process(Exchange exchange) throws Exception {
         ExecCommand execCommand = getBinding().readInput(exchange, endpoint);
 
+        ExecCommandExecutor executor = endpoint.getCommandExecutor();
+        if (executor == null) {
+            executor = new DefaultExecCommandExecutor(exchange);
+        }
+
         log.info("Executing {}", execCommand);
-        ExecResult result = endpoint.getCommandExecutor().execute(execCommand);
+        ExecResult result = executor.execute(execCommand);
+
         ObjectHelper.notNull(result, "The command executor must return a 
not-null result");
         log.info("The command {} had exit value {}", execCommand, 
result.getExitValue());
         if (result.getExitValue() != 0) {

http://git-wip-us.apache.org/repos/asf/camel/blob/bbbb3942/components/camel-exec/src/main/java/org/apache/camel/component/exec/impl/DefaultExecCommandExecutor.java
----------------------------------------------------------------------
diff --git 
a/components/camel-exec/src/main/java/org/apache/camel/component/exec/impl/DefaultExecCommandExecutor.java
 
b/components/camel-exec/src/main/java/org/apache/camel/component/exec/impl/DefaultExecCommandExecutor.java
index 1de2782..33cf6a2 100644
--- 
a/components/camel-exec/src/main/java/org/apache/camel/component/exec/impl/DefaultExecCommandExecutor.java
+++ 
b/components/camel-exec/src/main/java/org/apache/camel/component/exec/impl/DefaultExecCommandExecutor.java
@@ -23,8 +23,10 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.util.List;
 
+import org.apache.camel.Exchange;
 import org.apache.camel.component.exec.ExecCommand;
 import org.apache.camel.component.exec.ExecCommandExecutor;
+import org.apache.camel.component.exec.ExecDefaultExecutor;
 import org.apache.camel.component.exec.ExecEndpoint;
 import org.apache.camel.component.exec.ExecException;
 import org.apache.camel.component.exec.ExecResult;
@@ -49,6 +51,12 @@ public class DefaultExecCommandExecutor implements 
ExecCommandExecutor {
 
     private static final Logger LOG = 
LoggerFactory.getLogger(DefaultExecCommandExecutor.class);
 
+    private final Exchange exchange;
+
+    public DefaultExecCommandExecutor(Exchange exchange) {
+        this.exchange = exchange;
+    }
+
     public ExecResult execute(ExecCommand command) {
         notNull(command, "command");
 
@@ -76,6 +84,25 @@ public class DefaultExecCommandExecutor implements 
ExecCommandExecutor {
             LOG.error("ExecException while executing command: " + 
command.toString() + " - " + ee.getMessage());
             throw new ExecException("Failed to execute command " + command, 
ee);
         } catch (IOException ioe) {
+            // workaround to ignore if the stream was already closes due some 
race condition in commons-exec
+            String msg = ioe.getMessage();
+            if ("Stream closed".equals(msg)) {
+                LOG.debug("Ignoring Stream closed IOException", ioe);
+                // if the size is zero, we have no output, so construct the 
result
+                // with null (required by ExecResult)
+                InputStream stdout = out.size() == 0 ? null : new 
ByteArrayInputStream(out.toByteArray());
+                InputStream stderr = err.size() == 0 ? null : new 
ByteArrayInputStream(err.toByteArray());
+
+                // use 0 as exit value as the executor didn't return the value
+                int exitValue = 0;
+                if (executor instanceof ExecDefaultExecutor) {
+                    // get the exit value from the executor as it captures 
this to work around the common-exec bug
+                    exitValue = ((ExecDefaultExecutor) 
executor).getExitValue();
+                }
+
+                ExecResult result = new ExecResult(command, stdout, stderr, 
exitValue);
+                return result;
+            }
             // invalid working dir
             LOG.error("IOException while executing command: " + 
command.toString() + " - " + ioe.getMessage());
             throw new ExecException("Unable to execute command " + command, 
ioe);
@@ -86,7 +113,7 @@ public class DefaultExecCommandExecutor implements 
ExecCommandExecutor {
     }
 
     protected DefaultExecutor prepareDefaultExecutor(ExecCommand execCommand) {
-        DefaultExecutor executor = new DefaultExecutor();
+        DefaultExecutor executor = new ExecDefaultExecutor();
         executor.setExitValues(null);
 
         if (execCommand.getWorkingDir() != null) {

http://git-wip-us.apache.org/repos/asf/camel/blob/bbbb3942/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecEndpointTest.java
----------------------------------------------------------------------
diff --git 
a/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecEndpointTest.java
 
b/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecEndpointTest.java
index 26ec23f..ebd4cfc 100644
--- 
a/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecEndpointTest.java
+++ 
b/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecEndpointTest.java
@@ -74,7 +74,6 @@ public class ExecEndpointTest extends 
AbstractJUnit4SpringContextTests {
 
         assertEquals(NO_TIMEOUT, e.getTimeout());
         assertEquals("test", e.getExecutable());
-        assertNotNull(e.getCommandExecutor());
         assertNotNull(e.getBinding());
     }
 
@@ -150,7 +149,6 @@ public class ExecEndpointTest extends 
AbstractJUnit4SpringContextTests {
         ExecEndpoint endpoint = 
createExecEndpoint(UnsafeUriCharactersEncoder.encode(uri));
         assertEquals(cmd, endpoint.getExecutable());
         assertNull(endpoint.getArgs());
-        assertNotNull(endpoint.getCommandExecutor());
 
         assertEquals(dir, endpoint.getWorkingDir());
     }
@@ -165,7 +163,6 @@ public class ExecEndpointTest extends 
AbstractJUnit4SpringContextTests {
 
         assertNull(endpoint.getArgs());
         assertNull(endpoint.getWorkingDir());
-        assertNotNull(endpoint.getCommandExecutor());
 
         assertEquals(executable, endpoint.getExecutable());
     }

http://git-wip-us.apache.org/repos/asf/camel/blob/bbbb3942/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecJava8IssueTest.java
----------------------------------------------------------------------
diff --git 
a/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecJava8IssueTest.java
 
b/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecJava8IssueTest.java
new file mode 100644
index 0000000..5fa78d4
--- /dev/null
+++ 
b/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecJava8IssueTest.java
@@ -0,0 +1,119 @@
+/**
+ * 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.camel.component.exec;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.UUID;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.commons.exec.OS;
+import org.apache.commons.io.FileUtils;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test to duplicate issues with Camel's exec command in Java 8 on Unix
+ * This issue appears to be caused by a race condition, so this test does not 
always fail
+ */
+public class ExecJava8IssueTest extends Assert {
+
+    private File tempDir;
+    private final String tempDirName = name();
+    private final String tempFileName = name();
+
+    @Before
+    public void setUp() {
+        tempDir = new File("target", tempDirName);
+        if (!(tempDir.mkdir())) {
+            fail("Couldn't create temp dir for test");
+        }
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        FileUtils.deleteDirectory(tempDir);
+    }
+
+    @Test
+    public void test() throws Exception {
+
+        if (!OS.isFamilyUnix()) {
+            System.err.println("The test 'CamelExecTest' does not support the 
following OS : " + System.getProperty("os.name"));
+            return;
+        }
+
+        String tempFilePath = tempDir.getAbsolutePath() + "/" + tempFileName;
+
+        final File script = File.createTempFile("script", ".sh", tempDir);
+
+        writeScript(script);
+
+        final String exec = "bash?args=" + script.getAbsolutePath() + " " + 
tempFilePath + "&outFile=" + tempFilePath;
+
+        DefaultCamelContext context = new DefaultCamelContext();
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:source")
+                        .to("file:" + tempDir.getAbsolutePath() + "?fileName=" 
+ tempFileName)
+                        .to("exec:" + exec)
+                        .process(new Processor() {
+                            @Override
+                            public void process(Exchange exchange) throws 
Exception {
+                                String output = 
exchange.getIn().getBody(String.class);
+                                assertEquals("hello world\n", output);
+                            }
+                        });
+
+            }
+        });
+
+        context.start();
+
+        ProducerTemplate pt = context.createProducerTemplate();
+        String payload = "hello";
+
+        pt.sendBody("direct:source", payload);
+    }
+
+    /**
+     * Creates a script which will append " world" to a file
+     */
+    private void writeScript(File script) throws IOException {
+        try (FileWriter fw = new FileWriter(script);
+             PrintWriter pw = new PrintWriter(fw)) {
+            String s = "echo \" world\" >> $1";
+            pw.print(s);
+        }
+    }
+
+    /**
+     * Returns a random UUID
+     */
+    private String name() {
+        return UUID.randomUUID().toString();
+    }
+}
\ No newline at end of file

Reply via email to