How to throw exceptions from interceptSendToEndpoint handler in camel - apache-camel

I want to simulate HTTP exceptions for testing purposes in an integration test setting. I am using interceptSendToEndpoint. From the handler I can log, modify headers or body, but I can't throw any exception back into the intercepted route. They just get logged and that's it. They are not caught by an onException handler or doTry..doCatch block of the intercepted route where the code is that I actually want to test.
So my handler looks like
interceptSendToEndpoint("undertow:*").when(method("exTest", "enabled"))
.throwException(new Exception(("my Exception")))
Can somebody help me out here? I am still on camel 2.25? Is this different between 2.x and 3.x?

If you're talking about unit testing then you should use adviceWith with weaveByToUri or weaveById to replace the http-endpoint with your custom logic that you can then configure to throw the exception.
As far as I understand interceptSendToEndpoint isn't really intended to be used like this in unit testing.
Example:
package com.example;
import org.apache.camel.RoutesBuilder;
import org.apache.camel.builder.AdviceWithRouteBuilder;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.Test;
public class ExampleTest extends CamelTestSupport {
#Test
public void testHttpErrorHandling() throws Exception {
context.getRouteDefinition("testRoute").adviceWith(context, new AdviceWithRouteBuilder(){
#Override
public void configure() throws Exception {
weaveByToUri("http*")
.replace()
.throwException(Exception.class, "Test Exception");
weaveById("logHttpExceptionEndpoint")
.after()
.to("mock:result");
}
});
MockEndpoint resultMockEndpoint = getMockEndpoint("mock:result");
resultMockEndpoint.expectedMessageCount(1);
resultMockEndpoint.message(0).exchangeProperty("CamelExceptionCaught")
.isInstanceOf(Exception.class);
startCamelContext();
template.sendBody("direct:test", "");
resultMockEndpoint.assertIsSatisfied();
}
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new RouteBuilder(){
#Override
public void configure() throws Exception {
from("direct:test")
.routeId(("testRoute"))
.onException(Exception.class)
.log("caught exception.").id("logHttpExceptionEndpoint")
.handled(true)
.end()
.to("http:somewebsite.com")
.log("Hello from test");
}
};
}
#Override
public boolean isUseAdviceWith() {
return true;
}
}
As for integration testing you can use a stub or something like mockoon to simulate the web-services to see how they handle different kinds of exceptions outside unit tests.

Related

Apache Camel - call simple language without side effect

Is it possible to call an object with the Simple language directly within the route and without side effects? The 2 approaches i've tried are;
.toD("${header.exchangeHelper.inc1()}") //works but fails if there is a return type from the called method
.bean(new Simple("${header.exchangeHelper.inc1()}")) //works but sets the body to false
Neither of which give the ideal solution.
You can store the result to exchange property or header instead. This way you'll keep the original body and get the result from your method in case you need it later. Alternatively you can just call the method using a processor.
These are generally better approaches with Java-DSL for something like this than using simple-language since they benefit from IDE's refactoring tools, error highlighting and many forms of linting.
package com.example;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.apache.camel.Exchange;
import org.apache.camel.RoutesBuilder;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.Test;
public class ExampleTest extends CamelTestSupport {
ExchangeHelper exchangeHelper = mock(ExchangeHelper.class);
#Test
public void useSetPropertyTest() throws Exception {
MockEndpoint resultMockEndpoint = getMockEndpoint("mock:result");
resultMockEndpoint.expectedMessageCount(1);
resultMockEndpoint.message(0).body().isEqualTo("Hello");
when(exchangeHelper.inc1()).thenReturn(true);
template.sendBodyAndHeader("direct:useSetProperty", "Hello",
"exchangeHelper", exchangeHelper);
verify(exchangeHelper, times(1)).inc1();
resultMockEndpoint.assertIsSatisfied();
}
#Test
public void justUseProcessorTest() throws Exception {
MockEndpoint resultMockEndpoint = getMockEndpoint("mock:result");
resultMockEndpoint.expectedMessageCount(1);
resultMockEndpoint.message(0).body().isEqualTo("Hello");
when(exchangeHelper.inc1()).thenReturn(true);
template.sendBody("direct:justUseProcessor", "Hello");
verify(exchangeHelper, times(1)).inc1();
resultMockEndpoint.assertIsSatisfied();
}
#Test
public void useHeaderFromProcessorTest() throws Exception {
MockEndpoint resultMockEndpoint = getMockEndpoint("mock:result");
resultMockEndpoint.expectedMessageCount(1);
resultMockEndpoint.message(0).body().isEqualTo("Hello");
when(exchangeHelper.inc1()).thenReturn(true);
template.sendBodyAndHeader("direct:useHeaderFromProcessor", "Hello",
"exchangeHelper", exchangeHelper);
verify(exchangeHelper, times(1)).inc1();
resultMockEndpoint.assertIsSatisfied();
}
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new RouteBuilder(){
#Override
public void configure() throws Exception {
from("direct:useSetProperty")
.setProperty("result")
.simple("${header.exchangeHelper.inc1()}")
.log("Body: ${body} Result header: ${exchangeProperty.result}")
.to("mock:result")
.removeProperty("result");
from("direct:justUseProcessor")
.process( ex -> { exchangeHelper.inc1(); })
.log("Body: ${body}")
.to("mock:result");
from("direct:useHeaderFromProcessor")
.process( ex -> {
ex.getMessage()
.getHeader("exchangeHelper", ExchangeHelper.class)
.inc1();
})
.log("Body: ${body}")
.to("mock:result");
}
};
}
interface ExchangeHelper {
public boolean inc1();
}
}
Not tried, but why not using the wiretap EIP to issue an extra (and separated!) request to your requestHelper method ?
Something like:
from("direct:demo")
.wireTap("bean:${header.exchangeHelper.inc1()}")
.to("direct:doSomething");
You can simply use Camel Script, something like that:
from("direct:exampleScript")
.script().simple("${header.exchangeHelper.inc1()}")
.log("End")
;

Apache-camel corda component connection error: Failed to create route route1

I am trying the connect to Corda using that component and sending data to Apache ActiveMQ again using Apache Camel's Corda component.
Corda is running properly. Particularly, cardapp-example is running, and Notary- PartyA - PartyB and PartyC are alive. I can query using their terminal.
ActiveMQ is working properly, I test it with another input source.
I've also tried to connect difeerent localhost ports of all four nodes, and also the example one showed in the Camel's corda component webpage.
public class CordaConnector {
public void ConnectToCorda() throws Exception {
CamelContext context = new DefaultCamelContext();
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
context.addComponent("jms", JmsComponent.jmsComponentAutoAcknowledge(connectionFactory));
context.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
from("corda://localhost:10004?username=user1&password=test&operation=VAULT_TRACK&contractStateClass=#contractStateClass").
}
});
while(true) {
context.start();
}
}
}
I got the following error message:
Exception in thread "main" org.apache.camel.FailedToCreateRouteException: Failed to create route route1: Route(route1)[From[corda://localhost:10004?username=user1&pa... because of Failed to resolve endpoint: corda://localhost:10004?contractStateClass=%23contractStateClass&operation=VAULT_TRACK&password=test&username=user1 due to: Error binding property (contractStateClass=#contractStateClass) with name: contractStateClass on bean: org.apache.camel.component.corda.CordaConfiguration#1de76cc7 with value: #contractStateClass
...
So when tested seperately, corda works properly, ActiveMQ works properly (with different output), and I ave tried different ports to query information. I have alos tried different commands to query, such as:
from("corda://localhost:10000?username=user1&password=test&operation=NETWORK_MAP_FEED").
to("activemq:queue:try");
I've checked this question Failed to create route route1, but was no help.
I would appreciate any help on what might be the reason.
In your route from uri, you are setting the contractStateClass property using value #contractStateClass : this references a bean named contractStateClass in the Camel registry. But since you did not bind any bean with this name in the context registry, Camel fail to resolve this value: Error binding property (contractStateClass=#contractStateClass) with name: contractStateClass on bean: org.apache.camel.component.corda.CordaConfiguration#1de76cc7 with value: #contractStateClass
You simply need to configure a bean of type Class and provide it to the camel registry. Something like that should work ( camel version 2.24.x )
import net.corda.core.contracts.OwnableState;
import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.impl.SimpleRegistry;
public class CordaConnector {
public static void main(String[] args) {
try {
SimpleRegistry registry = new SimpleRegistry();
registry.put("contractStateClass", OwnableState.class);
CamelContext camelContext = new DefaultCamelContext(registry);
camelContext.addRoutes(new RouteBuilder() {
#Override
public void configure() {
from("corda://localhost:10004?username=user1&password=test&operation=VAULT_TRACK&contractStateClass=#contractStateClass")
.log("got message");
}
});
camelContext.start();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
EDIT for Camel v3.x :
import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.support.SimpleRegistry;
public class CordaConnector {
public static void main(String[] args) {
try {
SimpleRegistry registry = new SimpleRegistry();
registry.bind("contractStateClass", MyContractClass.class);
CamelContext camelContext = new DefaultCamelContext(registry);
camelContext.addRoutes(new RouteBuilder() {
#Override
public void configure() {
from("corda://localhost:10004?username=user1&password=test&operation=VAULT_TRACK&contractStateClass=#contractStateClass")
.log("got message");
}
});
camelContext.start();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}

Unit testing log nodes in Apache camel routes

Suppose I have the following camel route:
.from("direct:start")
.log("received ${body} message")
.to("mock:end");
How would you test that the message "received Camel rocks!" message is logged when you send a "Camel rocks!" message to the direct:start endpoint
I would read the written file. Or add a custom appender to the logging system and assert that it received the message.
Or check Camel's internal unit tests.
But what exactly are you trying to achieve?
You are supposed to test your application and not the frameworks you are using.
I would not test the actual logging part, but why not save the data you are interested in a property or header, and then in your unit test assert that the value of that property or header must be such and such?
Here is a way to test this which I admit is a bit too invasive. It would have been much easier if AdviceWithBuilder would have been added something like replaceWith(ProcessDefinition replacement).
Here is the working example:
package com.my.org.some.pkg;
import org.apache.camel.RoutesBuilder;
import org.apache.camel.builder.AdviceWithRouteBuilder;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.testng.CamelTestSupport;
import org.mockito.Mockito;
import org.slf4j.Logger;
import org.springframework.test.util.ReflectionTestUtils;
import org.testng.annotations.Test;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class CamelLoggingTest extends CamelTestSupport {
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:start").id("abc")
.log("received ${body} message")
.to("mock:stop");
}
};
}
#Test
public void shouldLogExpectedMessage() throws Exception {
Logger logger = Mockito.mock(Logger.class);
context.getRouteDefinition("abc").adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
ReflectionTestUtils.setField(context.getRouteDefinition("abc")
.getOutputs().get(0), "logger", logger);
}
});
when(logger.isInfoEnabled()).thenReturn(true);
sendBody("direct:start", "Camel rocks!");
verify(logger).info("received Camel rocks! message");
}
}

How to mock cxfbean call in Apache Camel?

I've got route that calls cxfbean:
.to("cxfbean:reservationService")
Tried mock this in my test with
#EndpointInject(uri = "mock:reservationService")
MockEndpoint reservationSystemMock;
#BeforeMethod
private void setUpContext() throws Exception
{
context.getRouteDefinition( "send.to.res.svc.endpoint" ).adviceWith(
context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception
{
interceptSendToEndpoint("cxfbean:reservationService")
.skipSendToOriginalEndpoint()
.to("mock:reservationService");
}
});
}
Also tried mock with weaveByToString( "**reservationService" ).replace().to( "mock:reservationService" );. In both cases I get:
Caused by: org.apache.camel.NoSuchBeanException: No bean could be found in the registry for: reservationService
I'd like to test my route without cxf bean instantiation. I'm using CamelTestSupport class as parent.
Managed to mock cxfbean endpoint with weaveByToString( "To[cxfbean:reservationService]" ):
#EndpointInject(uri = "mock:reservationService")
protected MockEndpoint reservationSystemMock;
#BeforeMethod
private void setUpContext() throws Exception
{
context.getRouteDefinition( "send.to.res.svc.endpoint" ).adviceWith(
context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
weaveByToString( "To[cxfbean:reservationService]" )
.replace().to( "mock:reservationService" );
}
});
}
Also seems that we can peek necessary expression for weaveByToString using context.getRouteDefinitions().get(0).toString() in debug watcher
Remember to turn on advice-with on your test class. If you use those annotations, then add #UseAdviceWith to the class.
And then start the camel context after you have advised, which
http://camel.apache.org/spring-testing.html
http://camel.apache.org/advicewith.html

Camel blueprint testing and cucumber

Is it possible to combine cucumber with CamelBlueprintTestSupport? I have my runner class:
#RunWith(Cucumber.class)
#CucumberOptions(monochrome=true,
format={ "pretty", "html:target/cucumber"},
features = "C:/Users/Developer/workspace_camel/SRV002_PatronInformation/src/test/resources/cucumber/asynchronousErrorHandling.feature")
public class RunFeature_SRV002_PatronInformationTest {
}
and my blueprint test class with the scenarios:
public class SRV002_PatronInformationScenarioTest extends CamelBlueprintTestSupport {
#Override
protected String getBlueprintDescriptor() {
return "/OSGI-INF/blueprint/blueprint.xml";
}
#Given("^client communicates asynchronous via socket$")
public void client_communicates_asynchronous_via_socket() throws Throwable {
System.out.println("test");
}
#When("^client posts message$")
public void an_error_occurs_inside_the_integration() throws Throwable {
String endpoint = "netty4:tcp://localhost:5000?sync=false&textline=true";
template.sendBody(endpoint, "test");
}
#Then("^the integration should not return response to the client$")
public void the_integration_should_not_return_the_error_to_the_client() throws Throwable {
System.out.println("test");
}
}
The problem now is that, when I run this I run into nullpointerexception at template.sendbody because the context, bundle and routes haven't started. For some reason it seems adding #RunWith(Cucumber) prevents the camel routes from starting.
Anyone knows how this can be solved? Thanks
Souciance
Ok so I managed to solve this.
For reference look here:
http://camel.465427.n5.nabble.com/How-to-test-routes-when-using-another-TestRunner-td5772687.html
Thanks to Gregor Lenz for the help.
Essentially the key here is that in your Camel BlueprintTestSupport class, inside the test method, that starts the given scenario you need to add this.setUp(). See the code below:
In Cucumber
SRVXXX_FileTransferCamelRunner filetransfer = new SRVXXX_FileTransferCamelRunner();
#Given("^an input file$")
public void an_input_file() throws Throwable {
endpoint.append("file:C:/Camel/input?fileName=input.txt");
}
#When("^client puts the file in the input directory$")
public void client_puts_the_file_in_the_input_directory() throws Throwable {
filetransfer.testPutFile(fileData.toString(), endpoint.toString());
}
#Then("^the integration should move the file to the output directory$")
public void the_integration_should_move_the_file_to_the_output_directory() throws Throwable {
String outputPath = "C:/Camel/output/input.txt";
filetransfer.testFileHasMoved(outputPath);
}
In Camel
#Test
public void testPutFile(String body, String endpoint) throws Exception {
this.setUp();
template.sendBody(endpoint,body);
Thread.sleep(2000);
assertFileNotExists(endpoint);
}

Resources