Apache Camel - split the list, call API and merge the body - apache-camel

I am new to Apache Camel and I would like to ask a few things. Basically, I want to call the API based on the resource ID list I have and combine all the responses from the API into one message. I have already tried AggregationStrategy, but it doesn't seem to be working. The message body always returns the last response from API and it doesn't keep the old list. I would be very grateful if you could give me some examples.
Route
from("file:C:\\Inbound")
//Get resourceID list and split it to call the API
.split(simple("${body}"))
.aggregationStrategy(new ArrayListAggregationStrategy())
.toD("{{api_url}}${body.resourceID}")
.log("After API call ${body}")
.end();
AggregationStrategy
public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
Object newBody = newExchange.getIn().getBody();
ArrayList<Object> list = null;
if (oldExchange == null) {
list = new ArrayList<Object>();
list.add(newBody);
newExchange.getIn().setBody(list);
return newExchange;
} else {
list = oldExchange.getIn().getBody(ArrayList.class);
list.add(newBody);
return oldExchange;
}}}

Below you'll find a basic example how to implement .split with AggregationStrategy that returns a Java List. It should be straightforward to apply to your specific scenario.
Routes.java
import org.apache.camel.builder.RouteBuilder;
public class Routes {
public static RouteBuilder routes() {
return
new RouteBuilder() {
public void configure() {
from("timer:mainRouteTrigger?repeatCount=1")
.routeId("MainRouteTrigger")
.setBody(constant("100\n200\n300"))
.to("direct:mainRoute")
.end()
;
from("direct:mainRoute")
.routeId("MainRoute")
.log("MainRoute BEGINS: BODY: ${body}")
.split(body().tokenize("\n"), new Aggregator())
.to("direct:singleRoute")
.end()
.log("MainRoute ENDS: BODY: ${body}")
.end()
;
from("direct:singleRoute")
.routeId("SingleRoute")
.log("SingleRoute BEGINS: BODY: ${body}")
.setBody(simple("this is a response for id ${body}"))
.log("SingleRoute ENDS: BODY: ${body}")
.end()
;
}
};
}
}
Aggregator.java
import org.apache.camel.AggregationStrategy;
import org.apache.camel.Exchange;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Aggregator implements AggregationStrategy {
#Override
public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
// first iteration
// oldExchange is null
// body of newExchange is String
if (oldExchange == null) {
List<String> newList = buildListFrom(newExchange);
newExchange.getMessage().setBody(newList, List.class);
return newExchange;
}
// second and subsequent iterations
// body of oldExchange is List<String>
// body of newExchange is String
List<String> oldList = oldExchange.getMessage().getBody(List.class);
List<String> newList = buildListFrom(newExchange);
List<String> combined = Stream.concat(oldList.stream(), newList.stream()).collect(Collectors.toList());
oldExchange.getMessage().setBody(combined);
return oldExchange;
}
private List<String> buildListFrom(Exchange exchange) {
String body = exchange.getMessage().getBody(String.class);
List<String> list = new ArrayList<String>();
list.add(body);
return list;
}
}
When executed the following expected outcome is logged:
AbstractCamelContext INFO Routes startup (total:3 started:3)
AbstractCamelContext INFO Started MainRouteTrigger (timer://mainRouteTrigger)
AbstractCamelContext INFO Started MainRoute (direct://mainRoute)
AbstractCamelContext INFO Started SingleRoute (direct://singleRoute)
AbstractCamelContext INFO Apache Camel 3.14.2 (camel-1) started in 133ms (build:14ms init:105ms start:14ms)
MainSupport INFO Waiting until complete: Duration max 5 seconds
MainRoute INFO MainRoute BEGINS: BODY: 100
200
300
SingleRoute INFO SingleRoute BEGINS: BODY: 100
SingleRoute INFO SingleRoute ENDS: BODY: this is a response for id 100
SingleRoute INFO SingleRoute BEGINS: BODY: 200
SingleRoute INFO SingleRoute ENDS: BODY: this is a response for id 200
SingleRoute INFO SingleRoute BEGINS: BODY: 300
SingleRoute INFO SingleRoute ENDS: BODY: this is a response for id 300
MainRoute INFO MainRoute ENDS: BODY: [this is a response for id 100, this is a response for id 200, this is a response for id 300]
MainSupport INFO Duration max seconds triggering shutdown of the JVM
AbstractCamelContext INFO Apache Camel 3.14.2 (camel-1) shutting down (timeout:45s)
AbstractCamelContext INFO Routes stopped (total:3 stopped:3)
AbstractCamelContext INFO Stopped SingleRoute (direct://singleRoute)
AbstractCamelContext INFO Stopped MainRoute (direct://mainRoute)
AbstractCamelContext INFO Stopped MainRouteTrigger (timer://mainRouteTrigger)
AbstractCamelContext INFO Apache Camel 3.14.2 (camel-1) shutdown in 34ms (uptime:5s54ms)
Note that
[this is a response for id 100, this is a response for id 200, this is a response for id 300]
Is a Java List that has been converted to a string automatically by .log.

If you want to see the resulting message AFTER the split, you have to put your .log(...) statement after the .end() of the 'split & join' process.
For better clarity, I recommend you to indent your code, eg:
.split(simple("${body}"))
.aggregationStrategy(new ArrayListAggregationStrategy())
.toD("{{api_url}}${body.resourceID}")
.log("Individual API call response: ${body}")
.end()
.log("Complete aggregation result: ${body}");

Related

For 400 bad request in cxfrs:rsclient in camel, the exchange is null. For 200 http status, the exchange object is set

Below is the code snippet to consume an api endpoint. For 200 http response, the exchange object contains the payload received. But for 400 response, the payload received is not set in exchange object. Is anything missing in the code below?
Exchange exchange = serviceProducer.send(endPoint, new Processor() {
public void process(Exchange exchange) throws Exception {
exchange.setPattern(ExchangePattern.InOut);
Message inMessage = exchange.getIn();
inMessage.setHeader(CxfConstants.CAMEL_CXF_RS_USING_HTTP_API, Boolean.TRUE);
inMessage.setHeader(Exchange.CONTENT_TYPE, "application/json");
inMessage.setHeader(Exchange.HTTP_METHOD, "POST");
inMessage.setHeader(Exchange.HTTP_QUERY, "clientId=" + ClientId);
inMessage.setBody(request);
inMessage.setHeader(CxfConstants.CAMEL_CXF_RS_RESPONSE_CLASS, SearchResponse.class);
inMessage.setHeader(Exchange.CONTENT_TYPE, "application/json");
}
});
SearchResponse searchResponse = (SearchResponse) exchange.getOut().getBody();
getOut() creates a blank output message. You need to use getIn() or getMessage().
SearchResponse searchResponse = (SearchResponse) exchange.getIn().getBody();
https://camel.apache.org/manual/latest/faq/using-getin-or-getout-methods-on-exchange.html#UsinggetInorgetOutmethodsonExchange-UsinggetInorgetOutmethodsonExchange

Log messages not printed in Camel route

I made an instance of Camel Context and added the routes in the context as follows:
camelContext.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
System.out.println("in add route method");
from("direct:start").routeId("contextRoute")
.setHeader("client_id", constant("abc"))
.setHeader("client_secret", constant("clk"))
.setHeader(Exchange.HTTP_METHOD, constant("POST"))
.setHeader(Exchange.CONTENT_TYPE, constant("application/json"))
.setHeader(Exchange.HTTP_URI, constant(url))
.marshal(gsonDataFormat)
.log("trying to send message")
.to(url)
.log("response is ${body}");
}
});
camelContext.start();
However I am not able to get a log of my messages and debug the route. Is there an issue with the route or is there an issue with the log files or anything?

Camel asynchronous call get the response

I am making an asynchronous call to a webservice using apache camel API using below code:
Exchange exchange = new DefaultExchange(context);
Message msg = exchange.getIn();
msg.setBody(requestStr);
msg.setHeader("content-type", "application/json");
template.asyncCallback("direct:invokeAPI", exchange, new Synchronization() {
#Override
public void onComplete(Exchange exchange) {
System.out.println("Success");
System.out.println(exchange);
System.out.println(exchange.getIn());
System.out.println(exchange.getIn().getHeaders());
System.out.println(exchange.getIn().getBody());
System.out.println("================================");
System.out.println(exchange.getOut());
System.out.println(exchange.getOut().getHeaders());
System.out.println(exchange.getOut().getBody());
System.out.println("================================");
Exception ex = exchange.getException();
System.out.println(ex.getMessage());
}
#Override
public void onFailure(Exchange exchange) {
System.out.println("Failure");
}
});
Now my webservice is failing with an exception and it is returning a json response:
Response-Code: 500
Content-Type: application/json
Headers: {Content-Type=[application/json], Date=[Fri, 03 Aug 2018 13:18:19 GMT]}
Payload: {"errorMessage":"Unable to process","errorCode":"500"}
Now how can I capture this information in my template.asyncCallback method.
In my above code it is going to onComplete callback method and printing below messages :
Success
Exchange[ID-xxxx-0-2]
Message[ID-xxxx-0-1]
{breadcrumbId=ID-xxxx-0-1, content-type=application/json}
{
"source":"PDF"
}
================================
Message[]
{}
null
================================
I am trying to get the status 500 code and the response payload that has the error information, but I am not able to print anything. Can you please help me what is the correct way to get the error information.

Is it possible to to cache a response using cache-control in spring boot controller?

My requirement is to reuse some meta-data in browser level rather than requesting every time from server. My rest end point has written using spring boot and i have added Cache-control and max-age headers to my response. I had set max-age to 10 seconds. As per my understanding we can do this for fixed assets like css and js files. Is it possible to do with a object(response).
#CrossOrigin
#RequestMapping(value = "/retrieve/{name}", method = RequestMethod.GET)
public ResponseEntity<Course> getCourses(#PathVariable String name) {
Course course = new Course();
HttpHeaders headers = new HttpHeaders();
try {
System.out.println("Call is here.");
course = courseService.getCourse(name);
headers.add("Cache-control", "private");
headers.add("max-age", "10");
} catch (Exception e) {
return new ResponseEntity<>(course, HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(course, headers, HttpStatus.OK);
}
But in front end level the request is still going in to server and serves with a new response. I'm using angualr as my client side framework.I want to clear the response data after completing the max-age value that has mentioned in the response header.
this is the related angular component.
app.component('searchCourseComponent', {
bindings: {
name: '<',
},
templateUrl: 'components/course-search/course-search.component.html',
controller: function (localStorageService, CourseService) {
var vm = this;
vm.searchCourseByName = function (name) {
if (localStorageService.isSupported) {
console.log("localStrage is supporting in this browser");
var course = CourseService.searchCourse(name).then(function (response) {
localStorageService.set("course", response.data);
});
}
else {
console.log("Local storage is not supporting");
}
};
}
});
You can use local storage service in angularJs.
For this include the below in index.html
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-local-storage/0.7.1/angular-local-storage.min.js"></script>
Inject LocalStorageModule into app.module
angular.module('appName', ['LocalStorageModule'])
and while using in controller inject localStorageService into controller and set value you want to store into local storage.
For example: localStorageService.set("nameForStoringData",dataToBeSet);
You can use Angualr $http cache for this. It will not send same request again.
$http({
method: type,
url: url,
params: params,
cache: true,
responseType: responseType
}).then(function (response) {
// success response
}, function (response) {
// when something went wrong
});
Better way, create servlet filter and add headers to response what ever you need.
First create this configuration class:
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Configuration;
#Configuration
#EnableCaching
public class CacheConfiguration {
}
and this servlet filter:
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
public class CachingHttpHeadersFilter implements Filter {
// We consider the last modified date is the start up time of the server
private final static long LAST_MODIFIED = System.currentTimeMillis();
private long CACHE_TIME_TO_LIVE = TimeUnit.DAYS.toMillis(1461L);
#Override
public void init(FilterConfig filterConfig) throws ServletException {
CACHE_TIME_TO_LIVE = TimeUnit.DAYS.toMillis(365); //365 days
}
#Override
public void destroy() {
// Nothing to destroy
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setHeader("Cache-Control", "max-age=" + CACHE_TIME_TO_LIVE + ", public");
httpResponse.setHeader("Pragma", "cache");
// Setting Expires header, for proxy caching
httpResponse.setDateHeader("Expires", CACHE_TIME_TO_LIVE + System.currentTimeMillis());
// Setting the Last-Modified header, for browser caching
httpResponse.setDateHeader("Last-Modified", LAST_MODIFIED);
chain.doFilter(request, response);
}
}
and register this filter and add urlPatterns you need :
#Bean
public FilterRegistrationBean cachingHttpHeadersFilter() throws IOException {
log.info("Registering Caching HTTP Headers Filter");
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new CachingHttpHeadersFilter());
registration.setDispatcherTypes(EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ASYNC));
registration.setName("cachingHttpHeadersFilter");
List<String> urlPatterns = new ArrayList<>();
urlPatterns.add("/asset*");
registration.setUrlPatterns(urlPatterns);
return registration;
}
registered filter adds cache-control header to http requests association with /asset*
.

i need to make a java.net url fetch with a json request. Please tell me how can i call that using java in google appengine

This is my HTTP URL
POST HTTPS://www.googleapis.com/admin/directory/v1/group
MY json request
{
"email": "sales_group#example.com",
"name": "Sales Group",
"description": "This is the Sales group."
}
I am using Directory API to create groups.
I never used URL fetch so far so i am not familiar with that.
Please help me how can i do that..
THIS IS MY ANSWER I POSTED AFTER 2 HOURS. stackoverflow did not allow me to to answer my own question before 8 hours since i have less than 10 reputation, so forgive me for long question.
I tried this..
i was struggling a bit in passing json as parameter. here is my code
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.logging.Logger;
import javax.servlet.http.*;
import com.google.appengine.labs.repackaged.org.json.JSONException;
import com.google.appengine.labs.repackaged.org.json.JSONObject;
#SuppressWarnings("serial")
public class DirectoryApiExampleServlet extends HttpServlet {
static Logger log = gger.getLogger(DirectoryApiExampleServlet.class.getName());
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
URL url = new URL("https://www.googleapis.com/admin/directory/v1/groups");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Accept", "application/json");
OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
JSONObject json = new JSONObject();
try {
json.put("email", "abc#gmail.com");
json.put("name", "Test Group");
json.put("description", "this is a test group");
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
writer.write(json.toString());
writer.flush();
writer.close();
log.info("connection.getResponseCode()"+connection.getResponseCode());
}
}
But it is giving 401 response which is not expected.
Where am i making mistake???
There is documentation available on how to make a POST request. An example is also provided. Check out the following section : https://developers.google.com/appengine/docs/java/urlfetch/usingjavanet#Using_HttpURLConnection
You need to add authorization header with Access Token
Authorization : Bearer ya29.vQE48oltidkR

Resources