Reputation: 131
Is there an elegant way of unmarshaling a JSON to POJO in the Apache Camel using custom aggregation strategy?
My simple route takes a message from SQS, the message is JSON. This message is used as input to another service, which in turn based on the message content will let me know what is the URL of a service where the original message should be posted to.
Enrich EIP with AggregationStrategy is perfect for this. However, I cannot not figure out how to unmarshal JSON to POJO elegantly in the route. I am able to do this via ObjectMapper but this just looks ugly. Is there a better way of handling it? Some magical Camel sauce that I haven's seen yet?
public RouteBuilder route() {
return new RouteBuilder() {
@Override
public void configure() throws Exception {
from("aws-sqs://" + sqsName + "?amazonSQSClient")
.setHeader("Content-Type",simple("application/json"))
.enrich()
.simple("http4://localhost:8080/getUrl")
.aggregationStrategy(new AggregationStrategy() {
@Override
public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
String aPojoStr = newExchange.getIn().getBody(String.class);
ObjectMapper mapper = new ObjectMapper();
RestAnswererResponse responosePojo;
try {
responosePojo = mapper.readValue(aPojoStr, RestAnswererResponse.class);
} catch (JsonParseException e) {
throw new RuntimeException("Error parsing response to pojo", e);
} catch (JsonMappingException e) {
throw new RuntimeException("Error parsing response to pojo", e);
} catch (IOException e) {
throw new RuntimeException("Error parsing response to pojo", e);
}
oldExchange.getIn().setHeader("URL", responosePojo.getURL());
return oldExchange;
}
})
.toD("http4://${header" + "URL + "}/postToAck);
}
};
}
EDIT: Just to clarify bit more how my route needs to work:
Contents of the JSON message needs to be posted to service which, based on the context of the original message, determines final URL (this is context specific discovery service) of where the message should be posted. The discovery service returns only the URL of the final destination.
{"url":"somehost:port"}
Upvotes: 1
Views: 3446
Reputation: 3665
You can use the JSON data format to unmarshal the message, rather than messing around with ObjectMapper
.
When using enrich I've found it useful to create a direct
route which it calls. I find this easier to understand, mock, test and you can give it different error handling and redelivery policies.
from("aws-sqs://" + sqsName + "?amazonSQSClient")
.setHeader("Content-Type",simple("application/json"))
.enrich("direct:other", strategy)
.toD("http4://${header" + "URL + "}/postToAck);
from("direct:other")
.to("http4://localhost:8080/getUrl")
.unmarshal().json(JsonLibrary.Jackson, RestAnswererResponse.class);
All the strategy needs to do is extract the URL and set the header as required.
Upvotes: 8
Reputation: 27068
You don't need aggregation strategy to achieve what you want. This can be done using jsonpath
from("aws-sqs://" + sqsName + "?amazonSQSClient")
.setHeader("Content-Type",simple("application/json"))
.setHeader("Accept", simple("application/json"))
.setHeader(Exchange.HTTP_METHOD, constant("GET"))
.toD("http4://localhost:8080/getUrl")
.transform().jsonpath("url")
.toD("http4://${body}/postToAck");
Considering you have a field url in the json response that you get from localhost:8080/getUrl
.
Lastly to use jsonpath you need this dependency.
<!-- https://mvnrepository.com/artifact/org.apache.camel/camel-jsonpath -->
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jsonpath</artifactId>
<version>yourcamelversion</version>
</dependency>
Update :
from("aws-sqs://" + sqsName + "?amazonSQSClient")
.setHeader("Content-Type",simple("application/json"))
.setHeader("Accept", simple("application/json"))
.setHeader(Exchange.HTTP_METHOD, constant("POST"))
.toD("http4://localhost:8080/getUrl")
.convertBodyTo(String.class)
.unmarshal().json(JsonLibrary.Jackson, RestAnswererResponse.class)
.setHeader("url", simple("${body.url}"))
.setBody(simple("${body.originalBody}"))
.toD("${header.url}" + "/postToAck");
Considering
public class RestAnswererResponse {
String url;
String originalBody;
}
In your http4://localhost:8080/getUrl
endpoint, you should set the originalBody and then return. Something like this
class SomeController {
@PostMapping("/getUrl")
public RestResponse getUrl (@RequestBody String body) {
// based on the body object decide what is the url
return new RestResponse("url", body);// set the body it recieves as originalBody.
}
For json unmarshalling, you would need
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jackson</artifactId>
<version>yourcamelversion</version>
</dependency>
Upvotes: 0