Facing issue in reading the body in Camel REST service - java

I am developing 2 camel application :
Camel_1 application is deployable in server_1 and it need to make some rest calls to camel_2 application deployed in server_2.
Camel_1 :
public void configure() throws Exception {
from("file:C:/folder/IN")
.setBody(simple("${null}"))
.to("http://localhost:9090/camel/my-get-endpoint")
.log("Service body : ${body['filename']}")
Camel_2 :
#Override
public void configure() throws Exception {
restConfiguration()
.component("servlet")
.bindingMode(RestBindingMode.auto);
rest()
.get("/my-get-endpoint")
.route()
.outputType(String.class)
.to("mongodb:camelMongoClient?database=db&collection=myCollection&operation=findAll")
.log("Body Set ${body}")
.endRest()
}
}
Camel_2 returns body as :
[Document{{id=5ef11dd8ee96198e7bf4cb34, configured_email=abc#gmail.com, filename=[A-Z]{4} [A-Z|a-z_]{1,}((0[1-9])|(1[0-2]))((0[1-9])|(1[0-9])|(2[0-9])|(3[0-1]))_(19|20)\d{2}.(xls[mx]{1}|xls||csv)$}}]
Camel_1 also prints the same body but how do I transform this reponse so that I can pick the required fields and perform methods like ${body.size()}.

You might want to convert the BSON document MongoDB usually returns, into JSON. See if this list helps: MongoDB type conversions.

Converted the body into map and then I was able to access it.

Related

How to create an IAM User using apache camel?

I have a camel rest api. I was trying to create an IAM User using apache camel framework. The code is like so ->
.post("iam-create-user")
.route()
.process(new Processor(){
#Override
public void process(Exchange exchange) throws Exception {
exchange.getIn().setHeader("CamelAwsIAMUsername", "new-user");
})
.to("aws2-iam://current-user?accessKey=insert&secretKey=insert&operation=createUser")
.endRest();
I am getting this error java.lang.NullPointerException: null. What is the correct way of doing this? It shows in the camel docs to use a URL like so to("aws2-iam://test?iamClient=#amazonIAMClient&operation=createUser"). What do we put as iamClient?
The iamClient reported in the example (#amazonIAMClient) is an instance of IAMClient you'll need to put in the Camel registry with a bind name amazonIAMClient.
I think you need to specify region as aws-global for this particular component.
Maybe add the full stack trace of your error.

Apache Camel: Access original data (XML) returned from WebService

I am using Apache Camel to access a WebService.
I am trying to inject content into the data I receive from the WebService for testing purposes.
Using getBody(), I only can access the Java objects which were deserialized from the received XML. Does anybody know how I can access the original XML - before deserialization by Camel?
This is my route structure. As you can see, I create a proxy Webservice from bean eaigateway to the real WebService (bean webservice) I am querying.
from("cxf:bean:eaigateway")
.routeId(ROUTE_ID_GATEWAY)
.process(new EnrichBean())
.to(ENDPOINT_WEBSERVICE)
.process(new DisenrichBean())
;
from(ENDPOINT_WEBSERVICE)
.routeId(ROUTE_ID_WEBSERVICE)
.to("cxf:bean:webservice")
;
public class DisenrichBean implements Processor {
#Override
public void process(Exchange exchange) throws Exception {
// This contains the unserialized object returned from the webservice.
// But how to get and set the original XML content?
Object antwort = ((MessageContentsList)(exchange.getIn().getBody())).get(0);
}
}
Original message should be available from:
exchange.getUnitOfWork().getOriginalInMessage()
But in your case it seems like you need Object/XML marshalling.
Some example can be found here: http://camel.apache.org/data-format.html

Invoke route from Processor

I'm using Camel to integrate 2 systems. I have defined different routes and one of the routes consumes from a specific rabbitmq queue and send it to a REST service. Nothing fancy here, the route looks like this:
public class WebSurfingRabbitToRestRoute extends RouteBuilder{
#Override
public void configure() throws Exception {
from("rabbitmq://rabbit_host:port/Rabbit_Exchange").
setHeader("CamelHttpMethod", constant("POST")).
setHeader("Content-Type", constant("application/json")).
bean(TransformResponse.class, "transform").
to("http4://rest_service_host:port/MyRestService).
}
}
As you can see, i process every message before sending it to the rest service since i need to adjust some things. The problem comes when i find out that sometimes (i dont know how or when), the system that publish into rabbit, send 2 messages concatenated at once.
What i expect to get is a simple json like this:
[{field1:value1, field2:value2}]
What i sometimes get is:
[{field1:value1, field2:value2},{field1:value3, field2:value4}]
So when i face this scenario, the rest service im routing the message to, fails (obviously).
In order to solve this, i would like to know if there is a way to invoke a route from inside a processor. From the previous snippet of code you can see that Im calling the transform method, so the idea will be to do something like the following pseudo-code, because after the route is already fired, i cant split the events and send them both within the same route "instance", so i thought about invoking a different route that i can call from here which will send the message2 to the very same rest service.
public class TransformRabbitmqResponse {
public String transform(String body) throws Exception {
// In here i do stuff with the message
// Check if i got 2 messages concatenated
// if body.contains("},{") {
// split_messages
// InvokeDifferentRoute(message2)
//}
}
}
Do you guys think this is possible?
One option (though I am not sure this is the best option) would be to split this up into two different routes using a direct endpoint.
public class WebSurfingRabbitToRestRoute extends RouteBuilder{
#Override
public void configure() throws Exception {
from("rabbitmq://rabbit_host:port/Rabbit_Exchange")
.setHeader("CamelHttpMethod", constant("POST"))
.setHeader("Content-Type", constant("application/json"))
.bean(TransformResponse.class, "transform");
from("direct:transformedResponses")
.to("http4://rest_service_host:port/MyRestService");
}
}
And then in your transform bean, you can use camel Producer Template to publish the transformed payload(s) to your new direct endpoint (assuming you are using json?).
producerTemplate.sendBody("direct:transformedResponses", jsonString);

Spring Boot in standalone Tomcat ignores exceptions set in DeferredResults

I'm running a Spring Boot 1.2.1 application in standalone Tomcat.
I have two controller mappings, which both for simplicity always throw an exception. The first one is for a GET request and it returns a String for the view name:
#RequestMapping(value = {
"/index"
}, method = RequestMethod.GET)
public String init() throws MyRequestProcessingException {
if (true) {
throw new MyRequestProcessingException(
"Something went wrong processing request");
}
return "init";
}
This is the exception definition:
#ResponseStatus(value=HttpStatus.INTERNAL_SERVER_ERROR)
public class MyRequestProcessingException extends Exception {
public MyRequestProcessingException(String message) {
super(message);
}
}
In embedded Tomcat as well as in standalone Tomcat, trying to access /index always results in a 500 with some JSON error data being returned to the client.
My controller has another method which accepts a POST and returns a DeferredResult:
#RequestMapping(value = "html/start", method = RequestMethod.POST, consumes = APPLICATION_FORM_URLENCODED)
public DeferredResult<String> start(final HttpServletResponse response,
#Valid #ModelAttribute final InitialisationStartAttributes model,
final SessionData sessionExisting) throws MyRequestProcessingException {
final DeferredResult<String> finalResult = new DeferredResult<>(5000);
// Just return an error, so we can test
if (true) {
finalResult.setErrorResult(new MyRequestProcessingException(
"Something went wrong processing request"));
}
return finalResult;
}
In embedded Tomcat, a POST to /html/start returns a 500 with some JSON data in the response body, just like the other request method. However, when I try to reproduce this behaviour using a standalone Tomcat instance, I always get a 200 response with no response body.
I'm using Tomcat 8 in embedded and Tomcat 7.0.52 standalone, but I've also tried with standalone Tomcat 8 and it doesn't make a difference.
My application is deployed in the root application context by modifying /etc/tomcat/Catalina/localhost/ROOT.xml.
EDIT: I've done a bit more testing, and it does seem that DeferredResult is the culprit. I have yet to override handleErrorResult() to see what happens. I'm a bit surprised though, because I don't recall seeing anything in the documentation about the difference between returning a DeferredResult in embedded vs standalone Tomcat.
If you throw an exception in a #RequestMapping and don't handle it anywhere, then the response status is not set before you hit the ErrorPageFilter. If you want to map status to error path in your customizer, you need to handle the error and get the status set in the filter chain somewhere (e.g. with an #ExceptionHandler or by using an Exception with a #ResponseStatus). Another way to get your custom error page to render would be to map exceptions instead of (or as well as) the HttpStatus, using new ErrorPage(Exception.class, "/html/error").
This behaviour was due to a bug in Spring Boot's ErrorPageFilter; see bug report on GitHub. It was fixed in Spring Boot 1.2.3.

Apache Camel timeout during finding data through a CustomProcessor

I seem to get timeout errors after 20 seconds. I have a custom processor the implements Processor. I inject a DAO and when finding the data within the custom processor it takes longer to find the data on the Apache Camel side and it timeouts. If I run the same code without Apache Camel it runs instantly. By doing a SELECT inside the CustomProcessor it takes longer to find the data.
The memory reference for the DAO are the same, so in the test the data is fetched immediately and the CustomProcessor hangs for 20 seconds before the data is receieved and it throws an Exception.
I am unable to figure out what is the cause of the problem.
I have located the code on Githib: https://github.com/rajivj2/example2
The problem is on line 27 of StatusHibernateDAO. I use a in memory database with only one table. There is only data populated.
When using the CustomProcessor without Apache Camel it works perfectly.
I hope you can help.
Since you are using Annotation Based configuration, the embedded Active MQ broker might be stopped unexpectedly, it’s good to use external broker (ex: tcp://host: 61616).
It seems QoS settings on the JMS endpoint is not working. You can the Header values from ProducerTemplate.
Since you are using producerTemplate.requestBody the activemq end point assumes that it is Request and Replay exchange(InOut) and since there is no response from route timeout occurring. If you want to implement (InOut) follow the instructions at http://camel.apache.org/jms.html. Since your Routebuilder is InOnly, you need to send the disableReplyTo header from the ProducerTemplate.
Replace your test method testMessageSendToConsumerQueueRemoteId as
#Transactional
#Test
public void testMessageSendToConsumerQueueRemoteId() throws Exception {
Status status = new Status();
status.setUserId(10);
statusDAO.save(status);
Endpoint mockEndpoint = this.context.getEndpoint(properties.getProperty("activemq.destination"));
PollingConsumer consumer = mockEndpoint.createPollingConsumer();
producerTemplate.sendBodyAndHeader(source, "<?xml version='1.0' encoding='UTF-8' standalone='yes'?><example><remoteid>10</remoteid></example>","disableReplyTo","true");
Status savedStatus = consumer.receive(100).getIn().getBody(Status.class);
logger.info("savedStatus "+savedStatus.getID()+" "+savedStatus.getUserId());
assertNotNull(savedStatus);
}
Replace your ContentEnricherProcessor’s process method as
public void process(Exchange exchange) throws Exception {
Message message = exchange.getIn();
Entity entity = (Entity) message.getBody();
Status status = process(entity);
message.setBody(status);
exchange.getOut().setBody(status);
}
And your camel.property file should be
source=activemq:queue:deliverynotification
activemq.location=tcp://localhost:61616
activemq.destination=activemq:queue:responsenotification
If you want to receive back the generated response, you need to change your RouteBuilder.
from(properties.getProperty("source"))
.process(new ResponseProcessor())
.inOnly("direct:z")
.end();
from("direct:z")
.unmarshal()
.jaxb("com.example.entities.xml").convertBodyTo(Entity.class)
.multicast()
.to("direct:x")
.end();
from("direct:x").transacted()
.process((ContentEnricherProcessor) applicationContext.getBean("contentEnricherProcessor"))
.to(properties.getProperty("activemq.destination"));
Then change the producer patterns like
Status response = producerTemplate.requestBody(source, "<?xml version='1.0' encoding='UTF-8' standalone='yes'?><example><remoteid>11</remoteid></example>",Status.class);
Here is the ResponseProcessor process method
public void process(Exchange exchange) throws Exception {
Message outMsg = exchange.getIn().copy();
exchange.setOut(outMsg);
}
Camel ROCKS... you can implement almost any use case or pattern of Enterprise Integration :)

Categories