public boolean process(Exchange exchange, AsyncCallback callback) { // remember the callback to be used by the interceptor this.callback.set(callback); try { // invoke the target boolean done = target.process(exchange, callback); if (interceptorDone.get() != null) { // return the result from the interceptor if it was invoked return interceptorDone.get(); } else { // otherwise from the target return done; } } finally { // cleanup this.callback.remove(); this.interceptorDone.remove(); } }
private boolean doProcessSequential( final Exchange original, final AtomicExchange result, final Iterable<ProcessorExchangePair> pairs, final Iterator<ProcessorExchangePair> it, final ProcessorExchangePair pair, final AsyncCallback callback, final AtomicInteger total) { boolean sync = true; final Exchange exchange = pair.getExchange(); Processor processor = pair.getProcessor(); final Producer producer = pair.getProducer(); TracedRouteNodes traced = exchange.getUnitOfWork() != null ? exchange.getUnitOfWork().getTracedRouteNodes() : null; // compute time taken if sending to another endpoint final StopWatch watch = producer != null ? new StopWatch() : null; try { // prepare tracing starting from a new block if (traced != null) { traced.pushBlock(); } if (producer != null) { EventHelper.notifyExchangeSending(exchange.getContext(), exchange, producer.getEndpoint()); } // let the prepared process it, remember to begin the exchange pair AsyncProcessor async = AsyncProcessorConverterHelper.convert(processor); pair.begin(); sync = async.process( exchange, new AsyncCallback() { public void done(boolean doneSync) { // we are done with the exchange pair pair.done(); // okay we are done, so notify the exchange was sent if (producer != null) { long timeTaken = watch.stop(); Endpoint endpoint = producer.getEndpoint(); // emit event that the exchange was sent to the endpoint EventHelper.notifyExchangeSent( exchange.getContext(), exchange, endpoint, timeTaken); } // we only have to handle async completion of the routing slip if (doneSync) { return; } // continue processing the multicast asynchronously Exchange subExchange = exchange; // Decide whether to continue with the multicast or not; similar logic to the // Pipeline // remember to test for stop on exception and aggregate before copying back // results boolean continueProcessing = PipelineHelper.continueProcessing( subExchange, "Sequential processing failed for number " + total.get(), LOG); if (stopOnException && !continueProcessing) { if (subExchange.getException() != null) { // wrap in exception to explain where it failed subExchange.setException( new CamelExchangeException( "Sequential processing failed for number " + total, subExchange, subExchange.getException())); } else { // we want to stop on exception, and the exception was handled by the error // handler // this is similar to what the pipeline does, so we should do the same to not // surprise end users // so we should set the failed exchange as the result and be done result.set(subExchange); } // and do the done work doDone(original, subExchange, pairs, callback, false, true); return; } try { doAggregate(getAggregationStrategy(subExchange), result, subExchange); } catch (Throwable e) { // wrap in exception to explain where it failed subExchange.setException( new CamelExchangeException( "Sequential processing failed for number " + total, subExchange, e)); // and do the done work doDone(original, subExchange, pairs, callback, false, true); return; } total.incrementAndGet(); // maybe there are more processors to multicast while (it.hasNext()) { // prepare and run the next ProcessorExchangePair pair = it.next(); subExchange = pair.getExchange(); updateNewExchange(subExchange, total.get(), pairs, it); boolean sync = doProcessSequential(original, result, pairs, it, pair, callback, total); if (!sync) { LOG.trace( "Processing exchangeId: {} is continued being processed asynchronously", original.getExchangeId()); return; } // Decide whether to continue with the multicast or not; similar logic to the // Pipeline // remember to test for stop on exception and aggregate before copying back // results continueProcessing = PipelineHelper.continueProcessing( subExchange, "Sequential processing failed for number " + total.get(), LOG); if (stopOnException && !continueProcessing) { if (subExchange.getException() != null) { // wrap in exception to explain where it failed subExchange.setException( new CamelExchangeException( "Sequential processing failed for number " + total, subExchange, subExchange.getException())); } else { // we want to stop on exception, and the exception was handled by the error // handler // this is similar to what the pipeline does, so we should do the same to // not surprise end users // so we should set the failed exchange as the result and be done result.set(subExchange); } // and do the done work doDone(original, subExchange, pairs, callback, false, true); return; } // must catch any exceptions from aggregation try { doAggregate(getAggregationStrategy(subExchange), result, subExchange); } catch (Throwable e) { // wrap in exception to explain where it failed subExchange.setException( new CamelExchangeException( "Sequential processing failed for number " + total, subExchange, e)); // and do the done work doDone(original, subExchange, pairs, callback, false, true); return; } total.incrementAndGet(); } // do the done work subExchange = result.get() != null ? result.get() : null; doDone(original, subExchange, pairs, callback, false, true); } }); } finally { // pop the block so by next round we have the same staring point and thus the tracing looks // accurate if (traced != null) { traced.popBlock(); } } return sync; }
@Override public void onAfterRoute(Route route, Exchange exchange) { // we use the onAfterRoute callback, to ensure the data has been marshalled before // the consumer writes the response back // only trigger when it was the 1st route that was done if (!routeId.equals(route.getId())) { return; } // only marshal if there was no exception if (exchange.getException() != null) { return; } if (skipBindingOnErrorCode) { Integer code = exchange.hasOut() ? exchange.getOut().getHeader(Exchange.HTTP_RESPONSE_CODE, Integer.class) : exchange.getIn().getHeader(Exchange.HTTP_RESPONSE_CODE, Integer.class); // if there is a custom http error code then skip binding if (code != null && code >= 300) { return; } } boolean isXml = false; boolean isJson = false; // accept takes precedence if (accept != null) { isXml = accept.toLowerCase(Locale.ENGLISH).contains("xml"); isJson = accept.toLowerCase(Locale.ENGLISH).contains("json"); } // fallback to content type if still undecided if (!isXml && !isJson) { String contentType = ExchangeHelper.getContentType(exchange); if (contentType != null) { isXml = contentType.toLowerCase(Locale.ENGLISH).contains("xml"); isJson = contentType.toLowerCase(Locale.ENGLISH).contains("json"); } } // if content type could not tell us if it was json or xml, then fallback to if the binding // was configured with // that information in the consumes if (!isXml && !isJson) { isXml = produces != null && produces.toLowerCase(Locale.ENGLISH).contains("xml"); isJson = produces != null && produces.toLowerCase(Locale.ENGLISH).contains("json"); } // only allow xml/json if the binding mode allows that (when off we still want to know if its // xml or json) if (bindingMode != null) { isXml &= bindingMode.equals("off") || bindingMode.equals("auto") || bindingMode.contains("xml"); isJson &= bindingMode.equals("off") || bindingMode.equals("auto") || bindingMode.contains("json"); // if we do not yet know if its xml or json, then use the binding mode to know the mode if (!isJson && !isXml) { isXml = bindingMode.equals("auto") || bindingMode.contains("xml"); isJson = bindingMode.equals("auto") || bindingMode.contains("json"); } } // in case we have not yet been able to determine if xml or json, then use the same as in the // unmarshaller if (isXml && isJson) { isXml = wasXml; isJson = !wasXml; } // need to prepare exchange first ExchangeHelper.prepareOutToIn(exchange); // ensure there is a content type header (even if binding is off) ensureHeaderContentType(produces, isXml, isJson, exchange); if (bindingMode == null || "off".equals(bindingMode)) { // binding is off, so no message body binding return; } // is there any marshaller at all if (jsonMarshal == null && xmlMarshal == null) { return; } // is the body empty if ((exchange.hasOut() && exchange.getOut().getBody() == null) || (!exchange.hasOut() && exchange.getIn().getBody() == null)) { return; } try { // favor json over xml if (isJson && jsonMarshal != null) { jsonMarshal.process(exchange); } else if (isXml && xmlMarshal != null) { xmlMarshal.process(exchange); } else { // we could not bind if (bindingMode.equals("auto")) { // okay for auto we do not mind if we could not bind } else { if (bindingMode.contains("xml")) { exchange.setException( new BindingException( "Cannot bind to xml as message body is not xml compatible", exchange)); } else { exchange.setException( new BindingException( "Cannot bind to json as message body is not json compatible", exchange)); } } } } catch (Throwable e) { exchange.setException(e); } }
@Override public boolean process(Exchange exchange, final AsyncCallback callback) { if (enableCORS) { exchange.addOnCompletion(new RestBindingCORSOnCompletion(corsHeaders)); } boolean isXml = false; boolean isJson = false; String contentType = ExchangeHelper.getContentType(exchange); if (contentType != null) { isXml = contentType.toLowerCase(Locale.ENGLISH).contains("xml"); isJson = contentType.toLowerCase(Locale.ENGLISH).contains("json"); } // if content type could not tell us if it was json or xml, then fallback to if the binding was // configured with // that information in the consumes if (!isXml && !isJson) { isXml = consumes != null && consumes.toLowerCase(Locale.ENGLISH).contains("xml"); isJson = consumes != null && consumes.toLowerCase(Locale.ENGLISH).contains("json"); } // only allow xml/json if the binding mode allows that isXml &= bindingMode.equals("auto") || bindingMode.contains("xml"); isJson &= bindingMode.equals("auto") || bindingMode.contains("json"); // if we do not yet know if its xml or json, then use the binding mode to know the mode if (!isJson && !isXml) { isXml = bindingMode.equals("auto") || bindingMode.contains("xml"); isJson = bindingMode.equals("auto") || bindingMode.contains("json"); } String accept = exchange.getIn().getHeader("Accept", String.class); String body = null; if (exchange.getIn().getBody() != null) { // okay we have a binding mode, so need to check for empty body as that can cause the // marshaller to fail // as they assume a non-empty body if (isXml || isJson) { // we have binding enabled, so we need to know if there body is empty or not\ // so force reading the body as a String which we can work with body = MessageHelper.extractBodyAsString(exchange.getIn()); if (body != null) { exchange.getIn().setBody(body); if (isXml && isJson) { // we have still not determined between xml or json, so check the body if its xml based // or not isXml = body.startsWith("<"); isJson = !isXml; } } } } // favor json over xml if (isJson && jsonUnmarshal != null) { // add reverse operation exchange.addOnCompletion( new RestBindingMarshalOnCompletion( exchange.getFromRouteId(), jsonMarshal, xmlMarshal, false, accept)); if (ObjectHelper.isNotEmpty(body)) { return jsonUnmarshal.process(exchange, callback); } else { callback.done(true); return true; } } else if (isXml && xmlUnmarshal != null) { // add reverse operation exchange.addOnCompletion( new RestBindingMarshalOnCompletion( exchange.getFromRouteId(), jsonMarshal, xmlMarshal, true, accept)); if (ObjectHelper.isNotEmpty(body)) { return xmlUnmarshal.process(exchange, callback); } else { callback.done(true); return true; } } // we could not bind if (bindingMode == null || "off".equals(bindingMode) || bindingMode.equals("auto")) { // okay for auto we do not mind if we could not bind exchange.addOnCompletion( new RestBindingMarshalOnCompletion( exchange.getFromRouteId(), jsonMarshal, xmlMarshal, false, accept)); callback.done(true); return true; } else { if (bindingMode.contains("xml")) { exchange.setException( new BindingException( "Cannot bind to xml as message body is not xml compatible", exchange)); } else { exchange.setException( new BindingException( "Cannot bind to json as message body is not json compatible", exchange)); } callback.done(true); return true; } }
@Override public String toString() { return "AsyncBridge[" + interceptor.toString() + "]"; }
/** * Process invoked by the interceptor * * @param exchange the message exchange * @throws Exception */ public void process(Exchange exchange) throws Exception { // invoke when interceptor wants to invoke boolean done = interceptor.process(exchange, callback.get()); interceptorDone.set(done); }