Mocking XMPP endpoints - apache-camel

I'm trying to verify my Camel routes I need to prevent the endpoints from starting, the XMPP in particular as it contains concrete host information in their URI. Unfortunately I don't seem to figure out how.
My test class is as follows:
#RunWith(CamelSpringJUnit4ClassRunner.class)
#ContextConfiguration(
classes = {
ApplicationConfig.class
},
loader = CamelSpringDelegatingTestContextLoader.class)
#UseAdviceWith
#MockEndpointsAndSkip
public class XMPPRouteBuilderTest {
#Autowired
ApplicationContext applicationContext;
#Autowired
CamelContext camelContext;
#Test
public void testConfigure() throws Exception {
camelContext.start();
Collection<Endpoint> endpoints = camelContext.getEndpoints();
}
}
Whenever I call start() the actual endpoints are started which causes the XMPP routes to fail with host not found exceptions; I was expecting the mocks to replace the real ones.
Can anyone suggest what am I doing wrong?
Best,
Edoardo

#MockEndpointsAndSkip is only for producers (eg not consumers) so all the route from endpoints is not mocked.
You can use the replaceFromWith with the advice-with builder. See the section Replace from with another endpoint in the official Camel documentation for an example:
http://camel.apache.org/advicewith.html

Related

Explicitly define Camel Routes in Quarkus

Current behaviour: When I'm running a Quarkus App with Camel it automatically starts all the RouteBuilder Extensions as Routes.
What I want to achieve: On startup only the Routes that I configured are started.
What I tried:
With the following snippet it's possible to explicitly start the CamelMainApplication but I dont know how to get control over e.g. the CamelContext at this point where I would be able to configure my routes.
#QuarkusMain
public class Main {
public static void main(String[] args) throws Exception {
Quarkus.run(CamelMainApplication.class, args);
}
}
On the Route I can use .noAutoStartup() to disable the route on startup. But this means that it's not the default for all routes to be disabled at first and second I don't know where to activate them as I don't know where in a Quarkus App I can get a hand on the Camel Context to activate the route.
With the following in my application.yml I can disable the automatic route discovery but then the remaining question is how I can manually start the route, e.g. in my QuarkusMain class.
quarkus:
camel:
routes-discovery:
enabled: false
I think it is best way. Quarkus has properties for include and exclude route as pattern. this properties is List You can add one to N
quarkus.camel.routes-discovery.exclude-patterns=tes.Comp,tes.package.MyRoute
quarkus.camel.routes-discovery.include-patterns=test.mypackage.XX
I had this same problem I ended up doing something like the following:
#QuarkusMain
public class Main implements QuarkusApplication {
#Inject
OrderService orderService;
#Override
public int run(String... args) throws Exception {
CamelRuntime runtime = Arc.container().instance(CamelRuntime.class).get();
runtime.start(new String[]{});
orderService.handleOrders(args[0]); //this would inject the camelContext and start the route.
return 0;
}

Using Camel Endpoint DSL with #EndpointInject creating different endpoints

What is the proper way to use endpoint DSL and then reference the endpoint with ProducerTemplate? When creating a route and using endpoint DSL, it seems that Camel is creating a different uri for the endpoint. My EndpointRouteBuilder class:
#Component
public class MyRoutes extends EndpointRouteBuilder {
#Override
public void configure() throws Exception {
from(seda("STATUS_ENDPOINT"))
.routeId("stateChangeRoute")
.to(activemq("topic:statusTopic"))
}
}
and then injecting the endpoint to ProducerTemplate
#Component
public class StateChangePublisher {
#EndpointInject(value="seda:STATUS_ENDPOINT")
private ProducerTemplate producer;
public void publish(String str) {
try {
producer.sendBody(str);
} catch(CamelExecutionException e) {
e.printStackTrace();
}
}
}
When camel starts, I see two entries in the log:
o.a.camel.component.seda.SedaEndpoint : Endpoint seda:STATUS_ENDPOINT is using shared queue: seda:STATUS_ENDPOINT with size: 1000
o.a.camel.component.seda.SedaEndpoint : Endpoint seda://STATUS_ENDPOINT is using shared queue: seda://STATUS_ENDPOINT with size: 1000
The queue eventually fills up and nothing gets delivered to the "to" endpoint.
If I define the route without using the endpoint DSL method "seda()"
from("seda:STATUS_ENDPOINT")
then it works.
Is this a bug or am I doing something wrong?
I'm using camel 3.2.0 and
This was a bug in the endpoint dsl. Try upgrading to camel 3.3.0. I think it was fixed in the new release.
https://issues.apache.org/jira/browse/CAMEL-14859

Unit testing with Apache Camel

I want to test below camel route. All the example which i find online has route starting with file, where as in my case i have a spring bean method which is getting called every few minutes and finally message is transformed and moved to jms as well as audit directory.
I am clue less on write test for this route.
All i have currently in my test case is
Mockito.when(tradeService.searchTransaction()).thenReturn(dataWithSingleTransaction);
from("quartz2://tsTimer?cron=0/20+*+8-18+?+*+MON,TUE,WED,THU,FRI+*")
.bean(TradeService.class)
.marshal()
.jacksonxml(true)
.to("jms:queue:out-test")
.to("file:data/test/audit")
.end();
Testing with Apache Camel and Spring-Boot is really easy.
Just do the following (the example below is an abstract example just to give you a hint how you can do it):
Write a Testclass
Use the Spring-Boot Annotations to configure the test class.
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
#RunWith(SpringRunner.class)
public class MyRouteTest {
#EndpointInject(uri = "{{sourceEndpoint}}")
private ProducerTemplate sourceEndpoint;
....
public void test() {
// send your body to the endpoint. See other provided methods too.
sourceEndpoint.sendBody([your input]);
}
}
In the src/test/application.properties:
Configure your Camel-Endpoints like the source and the target:
sourceEndpoint=direct:myTestSource
Hints:
It's good not to hardwire your start-Endpoint in the route directly when using spring-boot but to use the application.properties. That way it is easier to mock your endpoints for unit tests because you can change to the direct-Component without changing your source code.
This means instead of:
from("quartz2://tsTimer?cron=0/20+*+8-18+?+*+MON,TUE,WED,THU,FRI+*")
you should write:
from("{{sourceEndpoint}}")
and configure the sourceEndpoint in your application.properties:
sourceEndpoint=quartz2://tsTimer?cron=0/20+*+8-18+?+*+MON,TUE,WED,THU,FRI+*
That way you are also able to use your route for different situations.
Documentation
A good documentation about how to test with spring-boot can be found here: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html
For Apache Camel: http://camel.apache.org/testing.html
#the hand of NOD Thanks for your hints, i was going into completely wrong direction. After reading your answer i was able to write the basic test and from this i think i can take it forward.
Appreciate your time, however i see that based on my route it should drop an XML file to audit directory which is not happening.
Look like intermediate steps are also getting mocked, without I specifying anything.
InterceptSendToMockEndpointStrategy - Adviced endpoint [xslt://trans.xslt] with mock endpoint [mock:xslt:trans.xslt]
INFO o.a.c.i.InterceptSendToMockEndpointStrategy - Adviced endpoint [file://test/data/audit/?fileName=%24%7Bheader.outFileName%7D] with mock endpoint [mock:file:test/data/audit/]
INFO o.a.camel.spring.SpringCamelContext - StreamCaching is not in use. If using streams then its recommended to enable stream caching. See more details at http://camel.apache.org/stream-caching.html
TradePublisherRoute.java
#Override
public void configure() throws Exception {
logger.info("TradePublisherRoute.configure() : trade-publisher started configuring camel route.");
from("{{trade-publisher.sourceEndpoint}}")
.doTry()
.bean(tradeService)
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
String dateStr = Constant.dateFormatForFileName.format(new Date());
logger.info("this is getting executed : " + dateStr);
exchange.setProperty(Constant.KEY_INCOMING_XML_FILE_NAME, "REQ-" + dateStr + Constant.AUDIT_FILE_EXTENSION);
exchange.setProperty(Constant.KEY_OUTGOING_XML_FILE_NAME, "RESP-" + dateStr + Constant.AUDIT_FILE_EXTENSION);
}
})
.marshal()
.jacksonxml(true)
.wireTap("{{trade-publisher.requestAuditDir}}" + "${header.inFileName}")
.to("{{trade-publisher.xsltFile}}")
.to("{{trade-publisher.outboundQueue}}")
.to("{{trade-publisher.responseAuditDir}}" + "${header.outFileName}")
.bean(txnService, "markSuccess")
.endDoTry()
.doCatch(Exception.class)
.bean(txnService, "markFailure")
.log(LoggingLevel.ERROR, "EXCEPTION: ${exception.stacktrace}")
.end();
TradePublisherRouteTest.java
#ActiveProfiles("test")
#RunWith(CamelSpringBootRunner.class)
#SpringBootTest(classes = TradePublisherApplication.class)
#MockEndpoints
public class TradePublisherRouteTest {
#EndpointInject(uri = "{{trade-publisher.outboundQueue}}")
private MockEndpoint mockQueue;
#EndpointInject(uri = "{{trade-publisher.sourceEndpoint}}")
private ProducerTemplate producerTemplate;
#MockBean
TradeService tradeService;
private List<Transaction> transactions = new ArrayList<>();
#BeforeClass
public static void beforeClass() {
}
#Before
public void before() throws Exception {
Transaction txn = new Transaction("TEST001", "C001", "100", "JPM", new BigDecimal(100.50), new Date(), new Date(), 1000, "P");
transactions.add(txn);
}
#Test
public void testRouteConfiguration() throws Exception {
Mockito.when(tradeService.searchTransaction()).thenReturn(new Data(transactions));
producerTemplate.sendBody(transactions);
mockQueue.expectedMessageCount(1);
mockQueue.assertIsSatisfied(2000);
}
Please correct me if i am doing something wrong!

Apache camel how to test a rollback scenario

I have a generic message router that has its routes created at run time by various route builders based on some configuration.
The configuration is stored as XML and when loaded in memory it gets converted into a RouteConfig domain object exposing via its getters how the route should be build. Such a RouteConfig will have a getFromUri(), getDestinations(), getDeadLetterUri(), isTransacted(), etc methods defined.
An example such a route builder would look like below:
public class NonTransactedRouteBuilder extends AbstractRouteBuilder {
#Override
protected void buildRoute(String endPoint, RouteConfig routeConfig) {
RouteBean routeBean = getRouteBean(routeConfig.getBean());
final String[] destinations = routeConfig.getDestinations();
from(endPoint).routeId(createRouteId())
.autoStartup(false)
.threads(routeConfig.getThreads())
.filter(body().isNotNull())
.process((Processor) routeBean)
.filter(body().isNotNull())
.choice()
.when(header("dead.letter").isNotNull())
.to(getDeadLetterUri())
.otherwise()
.loadBalance().random()
.to(destinations)
.endChoice();
}
}
The above is just an example to give you an idea about what kind of routes we build. The only one relevant thing is that the route is not transacted. Now to unit test it works as expected we extend the TestNG flavor of CamelTestSupport and pass a mocked RouteConfig instance that will result in a route being configured to move messages between a direct:test and two mock:test1 and mock:test2 end points. The route bean has a bit of logic in it and depending of the message content help us with all testing scenarios:
#Test
public void shouldDiscardNullMessages() throws Exception {
...
}
#Test
public void shouldDiscardScamMessages() throws Exception {
...
}
#Test
public void shouldRouteMessageToDeadLetterQueue() throws Exception {
...
}
#Test(timeOut = 1000)
public void shouldRouteMessagesInALoadBalancedWay()
...
}
Everything works fine and we are very happy we can make code changes and test them immediately. However most of our routes builders will build transacted routes. From our integration and end to end tests we know the transacted functionality works fine but would be so much better to be able to test this at your code changing point with an unit test.
So my question is:
Having the same route as above with just an autoStartup(false).transacted() change on it would it be a way to getting the message back in the sender direct:test end point so we can cover this part of the functionality as well. We would be happy with any work around suggestion just to prove this aspect works.
Thank you in advance for your inputs.
UPDATE 1:
One of the things I tried was to configure my test camel context with a TransactionErrorHandler that had a mock jta transaction manager injected. Something like bleow:
#Test
public void shouldBeAbleToRollback() throws Exception {
TransactionErrorHandlerBuilder errorHandlerBuilder = new TransactionErrorHandlerBuilder();
errorHandlerBuilder.setTransactionManager(jtaTransactionManagerMock);
context().setErrorHandlerBuilder(errorHandlerBuilder);
template.sendBody(FROM_1, "rollback message");
...
}
Then I hoped that I will be able to capture a jtaTransactionManagerMock.rollback() but this did not happen. Wat would be the reason not to work.
UPDATE 2:
Unable to achieve the above I stepped back and started integrating ActiveMQ as a transactional resource in my unit tests and everything worked fine. In reality our routes include also file and database end points but for the purpose of unit testing a generic queue builder using just a JMS resource is enough. I did not realized how easy would be when you deal all day long with Webshere MQ manager where you need to have MQ manager created and configured and you have to have the queues already there and all the heavy infrastructure that you have to build for this. ActiveMQ just did a very good job.
However I will still be interested in whether there would be an way to mock this transnational behavior.

Can I know which endpoint is finally targeted when using load balancer?

I have a route using a customized load balancer as,
from("timer://myTimer?period=2000")
.loadBalance(new MyCustomLoadBalancer())
.to("mock:em1").to("mock:em2").to("mock:em3")
.end();
In the customized balancer class, it seems only processors can be gotten.
public class MyCustomLoadBalancer extends SimpleLoadBalancerSupport {
public void process(Exchange exchange) throws Exception {
List<Processor> pList = getProcessors();
.......
//It is wanted to log which endpoint is finally targeted.
foo.process(exchange);
}
}
But here, I want to log actually which endpoint is targeted when using this load balancer.
In product environment, Jetty or HTTP endpoints will be used instead of these mock endpoints.
Is there a way to realized this?
===================================================================
Based on the suggestion from Ibsen, I used the Jetty endpoint to do test.
from("jetty:http://0.0.0.0:8043?matchOnUriPrefix=true")
.loadBalance(new MyCustomLoadBalancer())
.to("jetty:http://localhost:80?bridgeEndpoint=true&throwExceptionOnFailure=false")
.to("jetty:http://www.google.com?bridgeEndpoint=true&throwExceptionOnFailure=false")
.end();
But the Processors are not class of SendProcessor("foo instanceof SendProcessor" returns false), so I can't get the endpoint by getDestination.
I believe there should be some relationship between the endpoint and processor.
Could you give me more help?
Thanks.
The Processor is a SendProcessor where you can get the endpoint it will send the exchange to.
if (foo instanceof SendProcessor) {
SendProcessor send = (SendProcessor) foo;
Endpoint dest = send.getDestination();
...
}

Resources