@GET
@ManagedAsync
public void async(@Suspended final AsyncResponse async) {
final long time = System.nanoTime();
final AgentResponse response = new AgentResponse();
final CountDownLatch outerLatch = new CountDownLatch(2);
final Queue<String> errors = new ConcurrentLinkedQueue<>();
// Obtain visited destinations.
destination.path("visited").request()
// Identify the user.
.header("Rx-User", "Async")
// Async invoker.
.async()
// Return a list of destinations
.get(new InvocationCallback<List<Destination>>() {
@Override
public void completed(final List<Destination> destinations) {
response.setVisited(destinations);
outerLatch.countDown();
}
@Override
public void failed(final Throwable throwable) {
errors.offer("Visited: " + throwable.getMessage());
outerLatch.countDown();
}
});
// Obtain recommended destinations. (does not depend on visited ones)
destination.path("recommended").request()
// Identify the user.
.header("Rx-User", "Async")
// Async invoker.
.async()
// Return a list of destinations.
.get(new InvocationCallback<List<Destination>>() {
@Override
public void completed(final List<Destination> recommended) {
final CountDownLatch innerLatch = new CountDownLatch(recommended.size() * 2);
// Forecasts. (depend on recommended destinations)
final List<Forecast> forecasts = Collections.synchronizedList(
new ArrayList<Forecast>(recommended.size()));
for (final Destination dest : recommended) {
forecast.resolveTemplate("destination", dest.getDestination()).request()
.async()
.get(new InvocationCallback<Forecast>() {
@Override
public void completed(final Forecast forecast) {
forecasts.add(forecast);
innerLatch.countDown();
}
@Override
public void failed(final Throwable throwable) {
errors.offer("Forecast: " + throwable.getMessage());
innerLatch.countDown();
}
});
}
// Calculations. (depend on recommended destinations)
final List<Calculation> calculations = Collections.synchronizedList(
new ArrayList<Calculation>(recommended.size()));
for (final Destination dest : recommended) {
calculation.resolveTemplate("from", "Moon").resolveTemplate("to", dest.getDestination())
.request()
.async()
.get(new InvocationCallback<Calculation>() {
@Override
public void completed(final Calculation calculation) {
calculations.add(calculation);
innerLatch.countDown();
}
@Override
public void failed(final Throwable throwable) {
errors.offer("Calculation: " + throwable.getMessage());
innerLatch.countDown();
}
});
}
// Have to wait here for dependent requests ...
try {
if (!innerLatch.await(10, TimeUnit.SECONDS)) {
errors.offer("Inner: Waiting for requests to complete has timed out.");
}
} catch (final InterruptedException e) {
errors.offer("Inner: Waiting for requests to complete has been interrupted.");
}
// Recommendations.
final List<Recommendation> recommendations = new ArrayList<>(recommended.size());
for (int i = 0; i < recommended.size(); i++) {
recommendations.add(new Recommendation(recommended.get(i).getDestination(),
forecasts.get(i).getForecast(), calculations.get(i).getPrice()));
}
response.setRecommended(recommendations);
outerLatch.countDown();
}
@Override
public void failed(final Throwable throwable) {
errors.offer("Recommended: " + throwable.getMessage());
outerLatch.countDown();
}
});
// ... and have to wait also here for independent requests.
try {
if (!outerLatch.await(10, TimeUnit.SECONDS)) {
errors.offer("Outer: Waiting for requests to complete has timed out.");
}
} catch (final InterruptedException e) {
errors.offer("Outer: Waiting for requests to complete has been interrupted.");
}
if (errors.isEmpty()) {
response.setProcessingTime((System.nanoTime() - time) / 1000000);
async.resume(response);
} else {
final ArrayList<String> issues = new ArrayList<>(errors);
for (final String issue : issues) {