Bootstrap camel in spring web application - apache-camel

I'm creating a application with Spring-jersey-camel. I wanted to expose my jersey layer and internally invoke camel routes to invoke resources.
web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
applicationContext.xml
<camelContext id="camelContext" xmlns="http://camel.apache.org/schema/spring">
<packageScan>
<package>com.company.myapp.camel</package>
<excludes>**.*</excludes>
<includes>*Routes.java</includes>
</packageScan>
</camelContext>
MyRoutes.java
#Component
public final class MyRoutes extends RouteBuilder {
#Override
public void configure() throws Exception {
from("direct:getOrdersData").validate(body().isNotNull())
.log("Camel to get orders")
.to("restlet:http://localhost:8081/ordersapp/rest/order/123");
}
}
OrderResourceImpl.java
#Component
#Path("/orderLookup")
public class ReservationResources {
#org.apache.camel.produce
ProducerTemplate producer;
public void setProducer(ProducerTemplate producer) throws Exception {
this.producer = producer;
}
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("{orderId}")
public Response orderLookup(#PathParam("orderId") final long orderrId){
Response r = Response.noContent().build();
//Producer is null. throws nullPointerException
String order= producer.requestBody("direct:getOrdersData", orderId, String.class);
r = Response.ok().entity(reservation).build();
return r;
}
}
Any idea what I'm doing wrong? or how to inject myRoute/ProducerTemplate im my orderResourceImpl.java. Thanks in advance

Two Options,
If ReservationResources is a spring bean then, Inject the Camel Context into it and create a ProducerTemplate from that
ProducerTemplate template = camelContext.createProducerTemplate();
If ReservationResources is not a spring bean then get the Camel Context via a static method https://stackoverflow.com/a/13633109/3696510 and then create the ProducerTemplate.
ProducerTemplate template = StaticSpringApplicationContext.getBean("camelContext").createProducerTemplate()
Also if you do use that StaticSpringApplicationContext mentioned in the link, I would add this method to it.
public static <T> T getBean(String beanName, Class<T> clazz) {
return (T) CONTEXT.getBean(beanName,clazz);
}

Related

Working with apache camel Bean in Quarkus framework

I am trying to use apache camel with Quarkus. Previously I was using the spring-boot framework to develop camel integration. So there are lots of questions that I am still trying to figure out w.r.t. Quarkus framework.
Regarding: Bean
In spring-boot I could do something like this
#Configuration
public class JABXContextConfig {
#Bean
Unmarshaller jaxbUnmarshaller() throws JAXBException {
JAXBContext jaxbContext = JAXBContext.newInstance(MyPOJO.class );
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
return jaxbUnmarshaller;
}
}
and then I could inject it into the class using DI
#Component
public class MyRestServiceRoute extends RouteBuilder {
private final JaxbDataFormat jaxb;
#Autowired
public MyRestServiceRoute(JaxbDataFormat jaxb) throws Exception{
this.jaxb = jaxb;
}
....
}
QUESTION:
How can I do the same in the Quarkus framework?
P.S> I tried replacing #Configuration with #ApplicationScoped and #Bean with #Dependent but it's not working.
Thanks,
I recommend taking a read through the Quarkus CDI documentation:
https://quarkus.io/guides/cdi
https://quarkus.io/guides/cdi-reference
There's also a basic overview of using CDI to configure Camel:
https://camel.apache.org/camel-quarkus/latest/user-guide/bootstrap.html#_cdi
https://camel.apache.org/camel-quarkus/latest/user-guide/cdi.html
In your examples, #Bean could be replaced by a producer method like:
public class JaxbDataFormatProducer {
#ApplicationScoped
JaxbDataFormat jaxbDataFormat() {
return new JaxbDataFormat();
}
}
And the #Autowired constructor argument might look like this (If there’s only one constructor then there's actually no need for #Inject):
#ApplicationScoped
public class MyRestServiceRoute extends BaseRouteBuilder {
private final JaxbDataFormat jaxb;
#Inject
public MyRestServiceRoute(JaxbDataFormat jaxb) throws Exception{
super(properties);
this.jaxb = jaxb;
}
}

Simple Camel test fails with no messages recieved

Am using Spring Boot and I have just added camel to it.
I have a simple camel route setup :
import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;
#Component
public class MyRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("file://in").to("file://out");
}
}
When I try to create simple test for this route with :
#RunWith(CamelSpringBootRunner.class)
#SpringBootTest
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class MyRouteTest extends CamelTestSupport {
#Autowired
private CamelContext camelContext;
#Produce(uri = "file://in")
private ProducerTemplate producerTemplate;
#EndpointInject(uri = "mock:file://out")
private MockEndpoint mockEndpoint;
#Test
public void routeTest() throws Exception {
mockEndpoint.expectedMessageCount(1);
producerTemplate.sendBody("Test");
mockEndpoint.assertIsSatisfied();
}
}
It fails with
mock://file://out Received message count. Expected: <1> but was: <0>
Not sure what could be a problem here. I have producer template that has uri as my route from point and am mocking to endpoint with EndpointInject and the the mock uri?
Fixed but not 100%
If I change route from real one
from("file://in").to("file://out");
to
from("file://in").to("mock:out");
And in my test override
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new MyRoute();
}
to create specific route
and strangest of all ! Had to remove :
#SpringBootTest
and after that
private CamelContext camelContext;
And then it started working !
But unfortunately not what I need, still there are things that need to be fixed, I would like to use my real prod route !
from("file://in").to("file://out");
And if possible not use advise on route , but just mock it , tried with
mock:file://out in test, but it didnt work :(
and also , it does not work with #SpringBootTest ??? very strange ?!
You need to add
#Override
public String isMockEndpoints() {
return "*";
}
This should mock all the enpoints and then you can use mock:file:out for example
If I am not misstaken you are mocking your output endpoint yet your endpoint endpoint is a file endpoint. When you send a message you need to drop a message to whereever the file endpoint is polling. Otherwise you need to mock that as well.

camel return value from external Web Service

I need to invoke an external Web service running on WildFly from camel.
I managed to invoke it using the following route:
public class CamelRoute extends RouteBuilder {
final String cxfUri =
"cxf:http://localhost:8080/DemoWS/HelloWorld?" +
"serviceClass=" + HelloWorld.class.getName();
#Override
public void configure() throws Exception {
from("direct:start")
.id("wsClient")
.log("${body}")
.to(cxfUri + "&defaultOperationName=greet");
}
}
My question is how to get the return value from the Web service invocation ? The method used returns a String :
#WebService
public class HelloWorld implements Hello{
#Override
public String greet(String s) {
// TODO Auto-generated method stub
return "Hello "+s;
}
}
If the service in the Wild Fly returns the value then to see the values you can do the below
public class CamelRoute extends RouteBuilder {
final String cxfUri =
"cxf:http://localhost:8080/DemoWS/HelloWorld?" +
"serviceClass=" + HelloWorld.class.getName();
#Override
public void configure() throws Exception {
from("direct:start")
.id("wsClient")
.log("${body}")
.to(cxfUri + "&defaultOperationName=greet").log("${body}");
//beyond this to endpoint you can as many number of componenets to manipulate the response data.
}
}
The second log will log the response from the web service that you are returning. If you need to manipulate or do some routing and transformation with the response then you should look at the type of the response and accordingly you should use appropriate transformer.
Hope this helps.

Apache-Camel Accessing header values within string-template

my problem is, I don't know how I can access exchange's header values inside a string-template declaration. I would like to have internationalized mail templates. The test code below ...
public class StringTemplateTest extends CamelTestSupport {
#EndpointInject(uri = "mock:result")
protected MockEndpoint resultEndpoint;
#Produce(uri = "direct:start")
protected ProducerTemplate template;
#Test
public void testTemplating() throws Exception {
resultEndpoint.expectedBodiesReceived("test");
template.sendBodyAndHeader("test", "lang", "de");
resultEndpoint.assertIsSatisfied();
}
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
public void configure() {
from("direct:start").to("string-template:mailTemplate_$simple{in.header.lang}.tm").to("mock:result");
}
};
}
}
ends in a ...
java.io.FileNotFoundException: Cannot find resource: mailTemplate_$simple{in.header.lang}.tm in classpath for URI: mailTemplate_$simple{in.header.lang}.tm
I would expect, the string-template is lookig for mailTemplate_de.tm.
Thank you for help in advance!
Your problem is that .to("component:xyz") endpoints are evaluated at the time the route is built - they are not dynamic and won't pick up ${} properties.
Instead you need to use recipientList, like this:
from("direct:start")
.recipientList(simple("string_template:mailTemplate_${in.header.lang}.tm"))
.to("mock:result")

Migrating from CamelTestSupport to AbstractCamelTestNGSpringContextTests

I've been using Apache Camel since 3-4 months on Spring 4.0.7.RELEASE
I have several Camel 2.14.0 TestNG tests based on extending CamelTestSupport, in which I use some MockEndpoints.
I configured my routes by overriding the createRouteBuilder() method.
Now I would need also to inject some Spring beans in one of them, by #Autowired annotation.
By reading what is said at http://camel.apache.org/spring-testing.html, I understood that I've to extend AbstractCamelTestNGSpringContextTests now, which supports #Autowired, #DirtiesContext, and #ContextConfiguration.
While I understood that all MockEndpoints are no more accessible by getMockEndpoint() method, but by using #EndpointInject annotation, it is not clear to me is how I can express my routes, because createRouteBuilder() is not more available.
I saw that is possible to define producers and consumers by using annotations, but I cannot manage to understand how routes can be designed.
Many thanks to the community.
Alternatively to the solution given here, you may use the TestNG helper CamelSpringTestSupport in combination with AnnotationConfigApplicationContextif you want to initialize an annotated based Spring configuration context without the need of an additional XML Spring configuration file.
Camel configuration bean class using Spring annotations:
#Configuration
public class MyConfig extends SingleRouteCamelConfiguration {
#Bean
#Override
public RouteBuilder route() {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:test").to("mock:direct:end");
}
};
}
}
The TestNG test class extends CamelSpringTestSupport and the Spring configuration MyConfig is initialized with AnnotationConfigApplicationContext:
public class TestNGTest extends org.apache.camel.testng.CamelSpringTestSupport {
#EndpointInject(uri = "mock:direct:end")
protected MockEndpoint errorEndpoint;
#Produce(uri = "direct:test")
protected ProducerTemplate testProducer;
#Override
protected AbstractApplicationContext createApplicationContext() {
return new AnnotationConfigApplicationContext(MyConfig.class);
}
#DirtiesContext
#Test
public void testRoute() throws InterruptedException {
// use templates and endpoints
}
}

Resources