// Prepare for trace-level logging ...
if (stopwatch != null) stopwatch.start();
boolean abort = false;
RequestProcessor processorWithEvents = null;
try {
// ----------------------------------------------------------------------------------------------------
// Step 1: Fork the submitted requests into source-specific requests...
// ----------------------------------------------------------------------------------------------------
// This forks a subtask for each source, as soon as the first source-specific request for a source
// is generated. Each source's "execute(ExecutionContext,Request)" is called only once (a queue is
// used so that the source can begin processing the requests before all the requests have been
// computed and submitted to the subtask). Thus, it's possible (and likely) that this thread
// and subtask threads are executed in parallel.
final Queue<FederatedRequest> requests = awaitAllSubtasks ? new LinkedList<FederatedRequest>() : new LinkedBlockingQueue<FederatedRequest>();
final ForkRequestProcessor fork = new ForkRequestProcessor(repository, context, nowInUtc, requests);
if (synchronousStep1) {
// Execute the forking process in this thread ...
try {
fork.process(request);
} finally {
fork.close();
}
requests.add(new NoMoreFederatedRequests());
// At this point, all submitted requests have been processed/forked, so we can continue with
// the join process, starting with the first submitted request. Note that the subtasks may
// still be executing, but as the join process operates on a forked request, it will wait
// until all forked requests have completed. Hopefully, in most situations, the subtasks
// have enough of a lead that the join process never has to wait.
} else {
// Submit the forking process for execution in a separate thread ...
repository.getExecutor().submit(new Runnable() {
public void run() {
try {
fork.process(request);
} finally {
fork.close();
}
requests.add(new NoMoreFederatedRequests());
}
});
// At this point, the forking process is being run by a thread owned by the Executor. We'll still
// continue with the join process, starting with the first submitted request. Note that it is
// likely that the subtasks are still running in threads owned by the Executor.
}
if (awaitAllSubtasks) {
// Await until all subtasks have completed ...
fork.await();
}
// ----------------------------------------------------------------------------------------------------
// Step 2: Join the results of the source-specific (forked) requests back into the submitted requests
// ----------------------------------------------------------------------------------------------------
JoinRequestProcessor join = new JoinRequestProcessor(repository, context, observer, nowInUtc);
try {
if (awaitAllSubtasks) {
join.process(requests);
} else {
join.process((BlockingQueue<FederatedRequest>)requests);
}
} catch (RuntimeException e) {
abort = true;
throw e;
} finally {
if (!awaitAllSubtasks) {
// We need to guarantee that the fork processor is closed and released all its resources before we close
// ...
fork.await();
}
join.close();
processorWithEvents = join;
}
if (request instanceof CompositeRequest) {
// The composite request will not have any errors set, since the fork/join approach puts the
// contained requests into a separate CompositeRequest object for processing.
// So, look at the requests for any errors
((CompositeRequest)request).checkForErrors();
}
if (request.hasError() && !request.isReadOnly()) {
// There are changes and there is at least one failure ...
abort = true;
}
} catch (InterruptedException e) {
abort = true;
request.setError(e);
} catch (ExecutionException e) {
abort = true;
request.setError(e);
} catch (CancellationException e) {
abort = true;
request.cancel();
// do nothing else
} catch (RuntimeException e) {
abort = true;
throw e;
} finally {
if (stopwatch != null) stopwatch.stop();
if (abort) {
// Rollback the transaction (if there is one) ...
} else {
// fire off any notifications to the observer ...
assert processorWithEvents != null;
processorWithEvents.notifyObserverOfChanges();
}
}
}