/**
* 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.AdviceWithRouteBuilder;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.exec.impl.ProvokeExceptionExecCommandExecutor;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.converter.IOConverter;
import org.apache.camel.impl.JndiRegistry;
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")
ProducerTemplate producerTemplate;
@EndpointInject(uri = "mock:output")
MockEndpoint output;
@Override
public boolean isUseAdviceWith() {
return true;
}
@Override
protected JndiRegistry createRegistry() throws Exception {
JndiRegistry registry = super.createRegistry();
registry.bind("executorMock", new ProvokeExceptionExecCommandExecutor());
return registry;
}
@Test
public void testExecJavaProcessExitCode0() throws Exception {
context.start();
output.setExpectedMessageCount(1);
output.expectedHeaderReceived(EXEC_EXIT_VALUE, 0);
sendExchange(EXIT_WITH_VALUE_0, NO_TIMEOUT);
output.assertIsSatisfied();
}
@Test
public void testExecJavaProcessExitCode1() throws Exception {
context.start();
output.setExpectedMessageCount(1);
output.expectedHeaderReceived(EXEC_EXIT_VALUE, 1);
sendExchange(EXIT_WITH_VALUE_1, NO_TIMEOUT);
output.assertIsSatisfied();
}
@Test
public void testExecJavaProcessStdout() throws Exception {
context.start();
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 {
context.start();
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 {
context.start();
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 {
context.start();
// 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 {
context.start();
// 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);
assertEquals("Should be empty", "", out);
}
@Test
public void testConvertResultToInputStream() throws Exception {
context.start();
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 {
context.start();
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 {
context.start();
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 {
context.start();
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 {
context.start();
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 {
context.start();
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 {
context.start();
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 {
context.start();
int killAfterMillis = 1000;
output.setExpectedMessageCount(1);
// add some tolerance
output.setResultMinimumWaitTime(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 {
context.start();
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()));
}
/**
* Test for thrown {@link ExecException} and access stderr and exitValue
* of thrown Exception
*/
@Test
public void testExecJavaProcessWithThrownExecException() throws Exception {
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
@Override
public void configure() throws Exception {
weaveByToString(".*java.*").replace().to("exec:java?commandExecutor=#executorMock");
}
});
context.start();
output.setExpectedMessageCount(0);
Exchange out = sendFailExchange(EXIT_WITH_VALUE_0, NO_TIMEOUT);
//test if exitValue and stderr are accessible through thrown ExecException
ExecException ee = (ExecException) out.getException();
assertNotNull(ee.getExitValue());
assertNotNull(ee.getStderr());
output.assertIsSatisfied();
}
protected Exchange sendExchange(final Object commandArgument, final long timeout) {
return sendExchange(commandArgument, buildArgs(commandArgument), timeout, "testBody", false);
}
protected Exchange sendFailExchange(final Object commandArgument, final long timeout) {
return sendExchange(commandArgument, buildFailArgs(commandArgument), timeout, "testBody", false);
}
protected Exchange sendExchange(final Object commandArgument, final long timeout, final String body, final boolean useStderrOnEmptyStdout) {
return sendExchange(commandArgument, buildArgs(commandArgument), timeout, body, useStderrOnEmptyStdout);
}
protected Exchange sendExchange(final Object commandArgument, final List<String> args, final long timeout, final String body, final boolean useStderrOnEmptyStdout) {
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);
}
}
});
}
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;
}
/**
* Build arguments for execution which will result in error
*/
List<String> buildFailArgs(Object commandArgument) {
String classpath = System.getProperty("java.class.path");
List<String> args = new ArrayList<String>();
args.add("-failArg");
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");
}
};
}
}