/** | |
* 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.InputStream; | |
import java.util.ArrayList; | |
import java.util.List; | |
import org.apache.camel.EndpointInject; | |
import org.apache.camel.Exchange; | |
import org.apache.camel.Processor; | |
import org.apache.camel.Produce; | |
import org.apache.camel.ProducerTemplate; | |
import org.apache.camel.builder.RouteBuilder; | |
import org.apache.camel.component.mock.MockEndpoint; | |
import org.apache.camel.converter.IOConverter; | |
import org.apache.camel.test.junit4.CamelTestSupport; | |
import org.apache.commons.io.IOUtils; | |
import org.junit.Test; | |
import static org.apache.camel.component.exec.ExecBinding.EXEC_COMMAND_ARGS; | |
import static org.apache.camel.component.exec.ExecBinding.EXEC_COMMAND_EXECUTABLE; | |
import static org.apache.camel.component.exec.ExecBinding.EXEC_COMMAND_TIMEOUT; | |
import static org.apache.camel.component.exec.ExecBinding.EXEC_COMMAND_WORKING_DIR; | |
import static org.apache.camel.component.exec.ExecBinding.EXEC_EXIT_VALUE; | |
import static org.apache.camel.component.exec.ExecBinding.EXEC_STDERR; | |
import static org.apache.camel.component.exec.ExecBinding.EXEC_USE_STDERR_ON_EMPTY_STDOUT; | |
import static org.apache.camel.component.exec.ExecEndpoint.NO_TIMEOUT; | |
import static org.apache.camel.component.exec.ExecTestUtils.buildJavaExecutablePath; | |
import static org.apache.camel.component.exec.ExecutableJavaProgram.EXIT_WITH_VALUE_0; | |
import static org.apache.camel.component.exec.ExecutableJavaProgram.EXIT_WITH_VALUE_1; | |
import static org.apache.camel.component.exec.ExecutableJavaProgram.PRINT_ARGS_STDOUT; | |
import static org.apache.camel.component.exec.ExecutableJavaProgram.PRINT_IN_STDERR; | |
import static org.apache.camel.component.exec.ExecutableJavaProgram.PRINT_IN_STDOUT; | |
import static org.apache.camel.component.exec.ExecutableJavaProgram.READ_INPUT_LINES_AND_PRINT_THEM; | |
import static org.apache.camel.component.exec.ExecutableJavaProgram.SLEEP_WITH_TIMEOUT; | |
import static org.apache.camel.component.exec.ExecutableJavaProgram.THREADS; | |
import static org.apache.commons.io.IOUtils.LINE_SEPARATOR; | |
/** | |
* Tests the functionality of the {@link ExecComponent}, executing<br> | |
* <i>java org.apache.camel.component.exec.ExecutableJavaProgram</i> <br> | |
* command. <b>Note, that the tests assume, that the JAVA_HOME system variable | |
* is set.</b> This is a more credible assumption, than assuming that java is in | |
* the path, because the Maven scripts build the path to java with the JAVA_HOME | |
* environment variable. | |
* | |
* @see {@link ExecutableJavaProgram} | |
*/ | |
public class ExecJavaProcessTest extends CamelTestSupport { | |
private static final String EXECUTABLE_PROGRAM_ARG = ExecutableJavaProgram.class.getName(); | |
@Produce(uri = "direct:input") | |
private ProducerTemplate producerTemplate; | |
@EndpointInject(uri = "mock:output") | |
private MockEndpoint output; | |
@Test | |
public void testExecJavaProcessExitCode0() throws Exception { | |
output.setExpectedMessageCount(1); | |
output.expectedHeaderReceived(EXEC_EXIT_VALUE, 0); | |
sendExchange(EXIT_WITH_VALUE_0, NO_TIMEOUT); | |
output.assertIsSatisfied(); | |
} | |
@Test | |
public void testExecJavaProcessExitCode1() throws Exception { | |
output.setExpectedMessageCount(1); | |
output.expectedHeaderReceived(EXEC_EXIT_VALUE, 1); | |
sendExchange(EXIT_WITH_VALUE_1, NO_TIMEOUT); | |
output.assertIsSatisfied(); | |
} | |
@Test | |
public void testExecJavaProcessStdout() throws Exception { | |
String commandArgument = PRINT_IN_STDOUT; | |
output.setExpectedMessageCount(1); | |
output.expectedHeaderReceived(EXEC_EXIT_VALUE, 0); | |
Exchange e = sendExchange(commandArgument, NO_TIMEOUT); | |
ExecResult inBody = e.getIn().getBody(ExecResult.class); | |
output.assertIsSatisfied(); | |
assertEquals(PRINT_IN_STDOUT, IOUtils.toString(inBody.getStdout())); | |
} | |
@Test | |
public void testConvertResultToString() throws Exception { | |
String commandArgument = PRINT_IN_STDOUT; | |
output.setExpectedMessageCount(1); | |
Exchange e = sendExchange(commandArgument, NO_TIMEOUT); | |
output.assertIsSatisfied(); | |
String out = e.getIn().getBody(String.class); | |
assertEquals(PRINT_IN_STDOUT, out); | |
} | |
@Test | |
public void testByteArrayInputStreamIsResetInConverter() throws Exception { | |
String commandArgument = PRINT_IN_STDOUT; | |
output.setExpectedMessageCount(1); | |
Exchange e = sendExchange(commandArgument, NO_TIMEOUT); | |
String out1 = e.getIn().getBody(String.class); | |
// the second conversion should not need a reset, this is handled | |
// in the type converter. | |
String out2 = e.getIn().getBody(String.class); | |
output.assertIsSatisfied(); | |
assertEquals(PRINT_IN_STDOUT, out1); | |
assertEquals(out1, out2); | |
} | |
@Test | |
public void testIfStdoutIsNullStderrIsReturnedInConverter() throws Exception { | |
// this will be printed | |
String commandArgument = PRINT_IN_STDERR; | |
output.setExpectedMessageCount(1); | |
Exchange e = sendExchange(commandArgument, NO_TIMEOUT, null, true); | |
ExecResult body = e.getIn().getBody(ExecResult.class); | |
output.assertIsSatisfied(); | |
assertNull("the test executable must not print anything in stdout", body.getStdout()); | |
assertNotNull("the test executable must print in stderr", body.getStderr()); | |
// the converter must fall back to the stderr, because stdout is null | |
String stderr = e.getIn().getBody(String.class); | |
assertEquals(PRINT_IN_STDERR, stderr); | |
} | |
@Test | |
public void testStdoutIsNull() throws Exception { | |
// this will be printed | |
String commandArgument = PRINT_IN_STDERR; | |
output.setExpectedMessageCount(1); | |
Exchange e = sendExchange(commandArgument, NO_TIMEOUT, null, false); | |
ExecResult body = e.getIn().getBody(ExecResult.class); | |
output.assertIsSatisfied(); | |
assertNull("the test executable must not print anything in stdout", body.getStdout()); | |
assertNotNull("the test executable must print in stderr", body.getStderr()); | |
// the converter must fall back to the stderr, because stdout is null | |
String out = e.getIn().getBody(String.class); | |
assertNull("Should be null", out); | |
} | |
@Test | |
public void testConvertResultToInputStream() throws Exception { | |
String commandArgument = PRINT_IN_STDOUT; | |
output.setExpectedMessageCount(1); | |
Exchange e = sendExchange(commandArgument, NO_TIMEOUT); | |
output.assertIsSatisfied(); | |
InputStream out = e.getIn().getBody(InputStream.class); | |
assertEquals(PRINT_IN_STDOUT, IOUtils.toString(out)); | |
} | |
@Test | |
public void testConvertResultToByteArray() throws Exception { | |
String commandArgument = PRINT_IN_STDOUT; | |
output.setExpectedMessageCount(1); | |
Exchange e = sendExchange(commandArgument, NO_TIMEOUT); | |
output.assertIsSatisfied(); | |
byte[] out = e.getIn().getBody(byte[].class); | |
assertNotNull(out); | |
assertEquals(PRINT_IN_STDOUT, new String(out)); | |
} | |
@Test | |
public void testInvalidWorkingDir() throws Exception { | |
String commandArgument = PRINT_IN_STDOUT; | |
final List<String> args = buildArgs(commandArgument); | |
final String javaAbsolutePath = buildJavaExecutablePath(); | |
Exchange e = producerTemplate.send(new Processor() { | |
public void process(Exchange exchange) throws Exception { | |
exchange.getIn().setHeader(EXEC_COMMAND_EXECUTABLE, javaAbsolutePath); | |
exchange.getIn().setHeader(EXEC_COMMAND_WORKING_DIR, "\\cdd:///invalidWWorkginDir"); | |
exchange.getIn().setHeader(EXEC_COMMAND_ARGS, args); | |
} | |
}); | |
assertEquals(ExecException.class, e.getException().getClass()); | |
} | |
/** | |
* Test print in stdout from threads. | |
*/ | |
@Test | |
public void testExecJavaProcessThreads() throws Exception { | |
output.setExpectedMessageCount(1); | |
Exchange exchange = sendExchange(THREADS, NO_TIMEOUT); | |
String err = IOUtils.toString(exchange.getIn().getHeader(EXEC_STDERR, InputStream.class)); | |
ExecResult result = exchange.getIn().getBody(ExecResult.class); | |
String[] outs = IOUtils.toString(result.getStdout()).split(LINE_SEPARATOR); | |
String[] errs = err.split(LINE_SEPARATOR); | |
output.assertIsSatisfied(); | |
assertEquals(ExecutableJavaProgram.LINES_TO_PRINT_FROM_EACH_THREAD, outs.length); | |
assertEquals(ExecutableJavaProgram.LINES_TO_PRINT_FROM_EACH_THREAD, errs.length); | |
} | |
/** | |
* Test print in stdout using string as args | |
*/ | |
@Test | |
public void testExecJavaArgsAsString() throws Exception { | |
output.setExpectedMessageCount(1); | |
Exchange exchange = producerTemplate.send("direct:input", new Processor() { | |
public void process(Exchange exchange) throws Exception { | |
final String javaAbsolutePath = buildJavaExecutablePath(); | |
// use string for args | |
String classpath = System.getProperty("java.class.path"); | |
String args = "-cp \"" + classpath + "\" " + EXECUTABLE_PROGRAM_ARG + " " + PRINT_IN_STDOUT; | |
exchange.getIn().setBody("hello"); | |
exchange.getIn().setHeader(EXEC_COMMAND_EXECUTABLE, javaAbsolutePath); | |
exchange.getIn().setHeader(EXEC_COMMAND_ARGS, args); | |
exchange.getIn().setHeader(EXEC_USE_STDERR_ON_EMPTY_STDOUT, true); | |
} | |
}); | |
output.assertIsSatisfied(); | |
ExecResult result = exchange.getIn().getBody(ExecResult.class); | |
assertNotNull(result); | |
String out = IOConverter.toString(result.getStdout(), exchange); | |
assertEquals(PRINT_IN_STDOUT, out); | |
} | |
/** | |
* Test print in stdout using string as args with quotes | |
*/ | |
@Test | |
public void testExecJavaArgsAsStringWithQuote() throws Exception { | |
output.setExpectedMessageCount(1); | |
Exchange exchange = producerTemplate.send("direct:input", new Processor() { | |
public void process(Exchange exchange) throws Exception { | |
final String javaAbsolutePath = buildJavaExecutablePath(); | |
// use string for args | |
String classpath = System.getProperty("java.class.path"); | |
String args = "-cp \"" + classpath + "\" " + EXECUTABLE_PROGRAM_ARG + " " + PRINT_ARGS_STDOUT + " \"Hello World\""; | |
exchange.getIn().setBody("hello"); | |
exchange.getIn().setHeader(EXEC_COMMAND_EXECUTABLE, javaAbsolutePath); | |
exchange.getIn().setHeader(EXEC_COMMAND_ARGS, args); | |
exchange.getIn().setHeader(EXEC_USE_STDERR_ON_EMPTY_STDOUT, true); | |
} | |
}); | |
output.assertIsSatisfied(); | |
ExecResult result = exchange.getIn().getBody(ExecResult.class); | |
assertNotNull(result); | |
String out = IOConverter.toString(result.getStdout(), exchange); | |
assertTrue(out, out.contains("1Hello World")); | |
} | |
/** | |
* Test print in stdout using string as args with quotes | |
*/ | |
@Test | |
public void testExecJavaArgsAsStringWithoutQuote() throws Exception { | |
output.setExpectedMessageCount(1); | |
Exchange exchange = producerTemplate.send("direct:input", new Processor() { | |
public void process(Exchange exchange) throws Exception { | |
final String javaAbsolutePath = buildJavaExecutablePath(); | |
// use string for args | |
String classpath = System.getProperty("java.class.path"); | |
String args = "-cp \"" + classpath + "\" " + EXECUTABLE_PROGRAM_ARG + " " + PRINT_ARGS_STDOUT + " Hello World"; | |
exchange.getIn().setBody("hello"); | |
exchange.getIn().setHeader(EXEC_COMMAND_EXECUTABLE, javaAbsolutePath); | |
exchange.getIn().setHeader(EXEC_COMMAND_ARGS, args); | |
exchange.getIn().setHeader(EXEC_USE_STDERR_ON_EMPTY_STDOUT, true); | |
} | |
}); | |
output.assertIsSatisfied(); | |
ExecResult result = exchange.getIn().getBody(ExecResult.class); | |
assertNotNull(result); | |
String out = IOConverter.toString(result.getStdout(), exchange); | |
assertTrue(out, out.contains("1Hello")); | |
assertTrue(out, out.contains("2World")); | |
} | |
/** | |
* Test if the process will be terminate in about a second | |
*/ | |
@Test | |
public void testExecJavaProcessTimeout() throws Exception { | |
int killAfterMillis = 1000; | |
output.setExpectedMessageCount(1); | |
// add some tolerance | |
output.setMinimumResultWaitTime(800); | |
// max (the test program sleeps 60 000) | |
output.setResultWaitTime(30000); | |
sendExchange(SLEEP_WITH_TIMEOUT, killAfterMillis); | |
output.assertIsSatisfied(); | |
} | |
/** | |
* Test reading of input lines from the executable's stdin | |
*/ | |
@Test | |
public void testExecJavaProcessInputLines() throws Exception { | |
final StringBuilder builder = new StringBuilder(); | |
int lines = 10; | |
for (int t = 1; t < lines; t++) { | |
builder.append("Line" + t + LINE_SEPARATOR); | |
} | |
String whiteSpaceSeparatedLines = builder.toString(); | |
String expected = builder.toString(); | |
Exchange e = sendExchange(READ_INPUT_LINES_AND_PRINT_THEM, 20000, whiteSpaceSeparatedLines, false); | |
ExecResult inBody = e.getIn().getBody(ExecResult.class); | |
assertEquals(expected, IOUtils.toString(inBody.getStdout())); | |
} | |
protected Exchange sendExchange(final Object commandArgument, final long timeout) { | |
return sendExchange(commandArgument, timeout, "testBody", false); | |
} | |
protected Exchange sendExchange(final Object commandArgument, final long timeout, final String body, final boolean useStderrOnEmptyStdout) { | |
final List<String> args = buildArgs(commandArgument); | |
final String javaAbsolutePath = buildJavaExecutablePath(); | |
return producerTemplate.send(new Processor() { | |
public void process(Exchange exchange) throws Exception { | |
exchange.getIn().setBody(body); | |
exchange.getIn().setHeader(EXEC_COMMAND_EXECUTABLE, javaAbsolutePath); | |
exchange.getIn().setHeader(EXEC_COMMAND_TIMEOUT, timeout); | |
exchange.getIn().setHeader(EXEC_COMMAND_ARGS, args); | |
if (useStderrOnEmptyStdout) { | |
exchange.getIn().setHeader(EXEC_USE_STDERR_ON_EMPTY_STDOUT, true); | |
} | |
} | |
}); | |
} | |
private List<String> buildArgs(Object commandArgument) { | |
String classpath = System.getProperty("java.class.path"); | |
List<String> args = new ArrayList<String>(); | |
args.add("-cp"); | |
args.add(classpath); | |
args.add(EXECUTABLE_PROGRAM_ARG); | |
args.add(commandArgument.toString()); | |
return args; | |
} | |
@Override | |
protected RouteBuilder createRouteBuilder() { | |
return new RouteBuilder() { | |
public void configure() { | |
from("direct:input").to("exec:java").to("mock:output"); | |
} | |
}; | |
} | |
} |