Camel Java DSL: Update the next polling request param using the value from the response - apache

I am new to Apache camel, this is what I am trying to figure out. In a sample code below, I am trying to use the property - "value" in the request param in next polling request.
String valueFromTheResponse= ""
m.addRouteBuilder(new RouteBuilder() {
#Override
public void configure() throws Exception {
from("timer://foo?period=2)
.setHeader(Exchange.CONTENT_TYPE, constant("application/json"))
.setHeader("Accept", constant("application/json"))
.to("https4://" + <myrequestURL>?param=<valueFromTheResponse>)
.marshal().json(JsonLibrary.Jackson)
.setProperty("value", jsonpath("$.value"))
.process(new Processor() {
#Override
public void process(final Exchange exchange) throws Exception {
valueFromTheResponse = (String) exchange.getProperty("value");
}
})
}
});
m.run();
What would be the best way to achieve this? or assign the class level variable the property value?
UPDATE: SOLUTION
got it working by adding the following:
.process(new Processor() {
#Override
public void process(final Exchange exchange) throws Exception {
exchange.getIn().setHeader("CamelHttpQuery", buildParamQuery());
}
})

You would need to store the value in a shared field in for example the RouteBuilder class itself, or a shared class instance. And then in the to http endpoint uri, you need to set the param query as a message header instead where you can get that value via a method call.
.setHeader(Exchange.HTTP_QUERY, method(this, "buildParamQuery"))
And then have a method
public String buildParamQuery() {
return "param=" + sharedValue;
}
And then you set this field from the inlined processor with the last value. And mind about the initial value, eg the first poll the value is null so you need to maybe to return an empty string/null from the buildParamQuery method or something else.

Related

How to pass queue-name to camel route via a variable?

I am trying to dynamically create a rabbitmq queue via camel-route.
Camel route that I have configured:
#Override
public void configure() throws Exception {
JacksonDataFormat jsonDataFormat = new JacksonDataFormat(somequeue.class);
from("direct:startRabbitMQPoint").id("rabbitMQRoute").marshal(jsonDataFormat)
.to("rabbitmq:test.mymq.exchange?queue=test.mymq.queue&exchangeType=direct&autoDelete=false&connectionFactory=#myConnectFactory")
.end();
}
This configuration does create a queue with name test.mymq.queue. However, my requirement is to pass the queue name in a variable because I want to create queues dynamically. I tried passing a parameter to the route via the route-class constructor as shown below:
public class CreateQueueRoute extends RouteBuilder {
private String queueName;
public CreateQueueRoute(String queueName) {
this.queueName = queueName;
}
#Override
public void configure() throws Exception {
JacksonDataFormat jsonDataFormat = new JacksonDataFormat(somequeue.class);
from("direct:startRabbitMQPoint").id("rabbitMQRoute").marshal(jsonDataFormat)
.to("rabbitmq:test.mymq.exchange?queue="+queueName+"&exchangeType=direct&autoDelete=false&connectionFactory=#myConnectFactory")
.end();
}
This configuration creates a queue with the name "0 0/4 * * * ?"
What am I doing wrong? How can I pass the queue-name as a parameter to the camel route?
thanks in advance

Jersey ignores ExceptionMapper

I made an ExceptionMapper to catch and log all exceptions, like:
#Provider
public class CatchAllExceptionsMapper implements ExceptionMapper<Throwable> {
private static final Logger LOG = LoggerFactory.getLogger(CatchAllExceptionsMapper.class);
#Override
public Response toResponse(Throwable exception) {
LOG.error("Exception not catched!", exception);
return Response.serverError().build();
}
}
It catches the Exceptions my code throws, but if I send a Request with a JSON value that throws an IllegalStateException at my object's creation, this ExceptionMapper is ignored and I get a 400 Bad Request Response.
Funny thing is this Response is not the traditional Tomcat HTML formatted Response, its just plain text. It say just:
Cannot construct instance of `com.example.vo.AutoValue_Customer$Builder`, problem: First name is null or empty. at [Source: (org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream); line: 14, column: 1]
I thought this might be something short-circuiting Jersey, but my #PreMatching ContainerRequestFilter is executed beforehand, so I really have no idea why the 400 Response is not the traditional HTML one from Tomcat.
Why is this happening? What can I do to catch this and return my own Response?
As stated by Paul Samsotha in the comments, JacksonFeature from the jersey-media-json-jackson package define some ExceptionMappers, like JsonMappingException and JsonParseException. The solution is to create our own, register them within the ResourceConfig and register JacksonFeature last, otherwise it won't work.
e.g.
#Provider
#Priority(1) // hack for overriding other implementations.
public class JsonMappingExceptionMapper implements ExceptionMapper<JsonMappingException> {
#Override
public Response toResponse(JsonMappingException exception) {
return Response.status(Status.BAD_REQUEST).build();
}
}
#Provider
#Priority(1) // hack for overriding other implementations.
public class JsonParseExceptionMapper implements ExceptionMapper<JsonParseException> {
#Override
public Response toResponse(JsonParseException exception) {
return Response.status(Status.BAD_REQUEST).build();
}
}
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig() {
register(CatchAllExceptionsMapper.class);
register(JsonMappingExceptionMapper.class);
register(JsonParseExceptionMapper.class);
register(JacksonFeature.class);
}
}

camel custom marshalling with dataFormat name in header

I'm having two routes in two separated projects :
First route is setting the header with a data format bean name as a constant :
setHeader("dataFormatBeanName", constant("myFirstList"))
First route :
public class MyTest {
#Configuration
public static class MyTestConfig extends CamelConfiguration {
#Bean(name = "myFirstList")
public DataFormat getMyFirstListDataFormat() {
return new MyFirstListDataFormat();
}
#Bean(name = "mySecondList")
public DataFormat getMySecondListDataFormat() {
return new MySecondListDataFormat();
}
#Bean
public RouteBuilder route() {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:testFirstDataFormat").setHeader("dataFormatBeanName", constant("myFirstList")).to("direct:myRoute");
from("direct:testSecondDataFormat").setHeader("dataFormatBeanName", constant("mySecondList")).to("direct:myRoute");
}
};
}
}
}
Second route is supposed to retrieve the bean name from the header and use it as a custom marshaller. Something like :
custom(header("dataFormatBeanName"))
(doesn't compile)
Anyone knows how I'm supposed to get my bean name from the header to use it in the custom method ?
#Component
public class MyRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
final RouteDefinition routedefinition = this.from("direct:myRoute");
routedefinition.marshal().custom(??????????).to("netty4:tcp://{{route.address}}:{{port}}?textline=true&sync=true");
}
After a few more hours searching, here is the solution a found :
No changes in the first class.
Second class uses an anonymous DataFormat in which I retrieve the bean name from the header and get the spring bean from camel context before calling its marshal method.
The AbstractXxxDataFormat class belongs to project2 and is inherited by the Project1 DataFormat.
#Override
public void configure() throws Exception {
final RouteDefinition routedefinition = this.from("direct:myRoute");
routedefinition.marshal(new DataFormat() {
#Override
public void marshal(final Exchange exchange, final Object graph, final OutputStream stream) throws Exception {
AbstractXxxDataFormat myDataFormat = (AbstractGoalDataFormat) getContext().getRegistry().lookupByName(exchange.getIn().getHeader("dataFormatBeanName", String.class));
myDataFormat.marshal(exchange, graph, stream);
}
#Override
public Object unmarshal(final Exchange exchange, final InputStream stream) throws Exception {
return null;
}
});
routedefinition.to("netty4:tcp://{{route.address}}:{{port}}?textline=true&sync=true");
}
If there's any better solution available, I'll be interested.
Have you tried simple("${header.dataFormatBeanName}") to access the header?
Also, rather than passing the format bean name in a header in the first place, why not factor out each .marshal() call into two subroutes (one for formatBeanA and one for formatBeanB) and then call the appropriate subroute rather than setting the header in the first place? I believe this could be a cleaner approach.
If you really need to get it in the route as a variable (as opposed to a predicate to be used in the builder api) you could use an inline processor to extract it:
public class MyRouteBuilder extends RouteBuilder {
public void configure() throws Exception {
from("someEndpoint")
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
String beanName = exchange.getHeader("beanNameHeader");
}
});
}
}
Just be careful of scope and concurrency when storing the extracted beanName however.
A collegue of mine (thanks to him) found the definite solution :
set bean name in the exchange properties :
exchange.setProperty("myDataFormat", "myDataFormatAutowiredBean");
retrieve the dataFormat bean with RecipientList pattern and (un)marshal :
routedefinition.recipientList(simple("dataformat:${property.myDataFormat}:marshal"));
routedefinition.recipientList(simple("dataformat:${property.myDataFormat}:unmarshal"));
Very concise and works just fine.

Custom BeanNameResolver cannot handle a null base Object with identifier 'name'

I am trying to run EL 3.0 in standalone mode inside a servlet on Tomcat 8 using the following code -
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setAttribute("name", "Test1");
ELProcessor processor = new ELProcessor();
processor.getELManager().addBeanNameResolver(new BeanNameResolver() {
#Override
public Object getBean(String beanName) {
return request.getAttribute(beanName);
}
});
response.getWriter().write((String)processor.eval("name"));
}
Various articles on EL 3.0 mention that in order to resolve a bean it should be first defined using ELProcessor.defineBean(). However with custom bean resolver as defined above it should be able to resolve bean from request. When I run this code on tomcat 8, it shows following error -
javax.el.PropertyNotFoundException: ELResolver cannot handle a null base Object with identifier 'name'
org.apache.el.parser.AstIdentifier.getValue(AstIdentifier.java:100)
org.apache.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:187)
javax.el.ELProcessor.getValue(ELProcessor.java:45)
javax.el.ELProcessor.eval(ELProcessor.java:38)
org.koyad.servlet.TestELServlet.doGet(TestELServlet.java:59)
javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
Could someone please explain this behaviour?
The standalone javax.el.ELProcessor will throw this exception if it finds an undefined variable name in the expression condition. You can not even write null checks for undefined variables:
${empty unknownVariable}
will throw an exception if this variable hasn't been set before this. If it has been set to null, then it will return true.
When you are providing a custom BeanNameResolver implementation, you need to override the other methods also. For example the isNameResolved method is used to indicate that your resolver actually solved this value.
I have a custom BeanNameResolver that uses a custom Environment class (basically a map, similar to your request):
public static class EnvironmentBeanNameResolver extends BeanNameResolver{
private Environment environment;
public EnvironmentBeanNameResolver( Environment environment ){
this.environment = environment;
}
#Override
public boolean isNameResolved( String beanName ){
return environment.containsAttribute( beanName );
}
#Override
public Object getBean( String beanName ){
return environment.getAttribute( beanName );
}
#Override
public void setBeanValue( String beanName, Object value ){
environment.setAttribute( beanName, value );
}
#Override
public boolean isReadOnly( String beanName ){
return false;
}
#Override
public boolean canCreateBean( String beanName ){
return true;
}
}
and I use it like this
ELProcessor processor = new ELProcessor();
processor.getELManager().addBeanNameResolver( new EnvironmentBeanNameResolver( environment ) );
boolean result = (boolean)processor.eval( "empty knownVariable" );

Where is the best way to log response in Restlet

I use Restlet with Jetty8.
The Jetty log all incoming calls.
I want to log all response data also, url and body.
Where is the best place to put the log code?
I thought createOutboundRoot is the place but I didn't figured out how to use it and couldn’t find any examples in the web.
I have never tried it but I would start with a Custom filter and override After handle, this appears to be the way Restlet itself does logging internally see the class LogFilter.
implements a log filter like this :
public class CustomLogFilter extends Filter {
public CustomLogFilter() {
super();
}
protected int beforeHandle(Request request, Response response) {
int returned = super.beforeHandle(request, response);
// Do specific log if needed
return returned;
}
protected void afterHandle(Request request, Response response) {
super.afterHandle(request, response);
// Do specific log if needed
}
}
and use it in your createInboundRoot if you have an Application object :
public synchronized Restlet createInboundRoot() {
final Router router = new Router(getContext());
CustomLogFilter filter = new CustomLogFilter();
filter.setNext(router);
return filter;
}