@Override public boolean process(Exchange exchange, AsyncCallback callback) { // use atomic integer to be able to pass reference and keep track on the values AtomicInteger index = new AtomicInteger(); AtomicInteger count = new AtomicInteger(); // Intermediate conversion to String is needed when direct conversion to Integer is not // available // but evaluation result is a textual representation of a numeric value. String text = expression.evaluate(exchange, String.class); try { int num = ExchangeHelper.convertToMandatoryType(exchange, Integer.class, text); count.set(num); } catch (NoTypeConversionAvailableException e) { exchange.setException(e); callback.done(true); return true; } // we hold on to the original Exchange in case it's needed for copies final Exchange original = exchange; // per-iteration exchange Exchange target = exchange; // set the size before we start exchange.setProperty(Exchange.LOOP_SIZE, count); // loop synchronously while (index.get() < count.get()) { // and prepare for next iteration // if (!copy) target = exchange; else copy of original target = prepareExchange(exchange, index.get(), original); boolean sync = process(target, callback, index, count, original); if (!sync) { LOG.trace( "Processing exchangeId: {} is continued being processed asynchronously", target.getExchangeId()); // the remainder of the routing slip will be completed async // so we break out now, then the callback will be invoked which then continue routing from // where we left here return false; } LOG.trace( "Processing exchangeId: {} is continued being processed synchronously", target.getExchangeId()); // increment counter before next loop index.getAndIncrement(); } // we are done so prepare the result ExchangeHelper.copyResults(exchange, target); LOG.trace("Processing complete for exchangeId: {} >>> {}", exchange.getExchangeId(), exchange); callback.done(true); return true; }
@Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent exceptionEvent) throws Exception { if (LOG.isTraceEnabled()) { LOG.trace("Exception caught at Channel: " + ctx.getChannel(), exceptionEvent.getCause()); } if (exceptionHandled) { // ignore subsequent exceptions being thrown return; } exceptionHandled = true; Throwable cause = exceptionEvent.getCause(); if (LOG.isDebugEnabled()) { LOG.debug("Closing channel as an exception was thrown from Netty", cause); } Exchange exchange = getExchange(ctx); AsyncCallback callback = getAsyncCallback(ctx); // the state may not be set if (exchange != null && callback != null) { // set the cause on the exchange exchange.setException(cause); // close channel in case an exception was thrown NettyHelper.close(exceptionEvent.getChannel()); // signal callback callback.done(false); } }
@Override public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { if (LOG.isTraceEnabled()) { LOG.trace("Channel closed: {}", ctx.getChannel()); } Exchange exchange = getExchange(ctx); AsyncCallback callback = getAsyncCallback(ctx); // remove state producer.removeState(ctx.getChannel()); // to keep track of open sockets producer.getAllChannels().remove(ctx.getChannel()); if (producer.getConfiguration().isSync() && !messageReceived && !exceptionHandled) { // To avoid call the callback.done twice exceptionHandled = true; // session was closed but no message received. This could be because the remote server had an // internal error // and could not return a response. We should count down to stop waiting for a response if (LOG.isDebugEnabled()) { LOG.debug( "Channel closed but no message received from address: {}", producer.getConfiguration().getAddress()); } exchange.setException( new CamelExchangeException( "No response received from remote server: " + producer.getConfiguration().getAddress(), exchange)); // signal callback callback.done(false); } }
public void done(boolean doneSync) { try { if (!doneSync) { // when done asynchronously then restore information from previous thread if (breadcrumbId != null) { MDC.put(MDC_BREADCRUMB_ID, breadcrumbId); } if (exchangeId != null) { MDC.put(MDC_EXCHANGE_ID, exchangeId); } if (messageId != null) { MDC.put(MDC_MESSAGE_ID, messageId); } if (correlationId != null) { MDC.put(MDC_CORRELATION_ID, correlationId); } if (camelContextId != null) { MDC.put(MDC_CAMEL_CONTEXT_ID, camelContextId); } } // need to setup the routeId finally if (routeId != null) { MDC.put(MDC_ROUTE_ID, routeId); } } finally { // muse ensure delegate is invoked delegate.done(doneSync); } }
@Override public boolean process(Exchange exchange, AsyncCallback callback) { try { exchange.removeProperty(propertyName); } catch (Exception e) { exchange.setException(e); } callback.done(true); return true; }
@Override public boolean process(Exchange exchange, AsyncCallback callback) { try { Object newBody = expression.evaluate(exchange, Object.class); if (exchange.getException() != null) { // the expression threw an exception so we should break-out callback.done(true); return true; } boolean out = exchange.hasOut(); Message old = out ? exchange.getOut() : exchange.getIn(); // create a new message container so we do not drag specialized message objects along // but that is only needed if the old message is a specialized message boolean copyNeeded = !(old.getClass().equals(DefaultMessage.class)); if (copyNeeded) { Message msg = new DefaultMessage(); msg.copyFrom(old); msg.setBody(newBody); // replace message on exchange ExchangeHelper.replaceMessage(exchange, msg, false); } else { // no copy needed so set replace value directly old.setBody(newBody); } } catch (Throwable e) { exchange.setException(e); } callback.done(true); return true; }
private void processResponse( Exchange exchange, Object body, SalesforceException ex, AsyncCallback callback) { final Message out = exchange.getOut(); if (ex != null) { exchange.setException(ex); } else { out.setBody(body); } // copy headers and attachments out.getHeaders().putAll(exchange.getIn().getHeaders()); out.getAttachments().putAll(exchange.getIn().getAttachments()); // signal exchange completion callback.done(false); }
@Override public void done(boolean doneSync) { try { NettyHttpMessage nettyMessage = exchange.hasOut() ? exchange.getOut(NettyHttpMessage.class) : exchange.getIn(NettyHttpMessage.class); if (nettyMessage != null) { HttpResponse response = nettyMessage.getHttpResponse(); if (response != null) { // the actual url is stored on the IN message in the getRequestBody method as its // accessed on-demand String actualUrl = exchange.getIn().getHeader(Exchange.HTTP_URL, String.class); int code = response.getStatus() != null ? response.getStatus().getCode() : -1; log.debug("Http responseCode: {}", code); // if there was a http error code (300 or higher) then check if we should throw an // exception if (code >= 300 && getConfiguration().isThrowExceptionOnFailure()) { // operation failed so populate exception to throw Exception cause = NettyHttpHelper.populateNettyHttpOperationFailedException( exchange, actualUrl, response, code, getConfiguration().isTransferException()); exchange.setException(cause); } } } } finally { // ensure we call the delegated callback callback.done(doneSync); } }
@Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent messageEvent) throws Exception { messageReceived = true; if (LOG.isTraceEnabled()) { LOG.trace("Message received: {}", messageEvent); } if (producer.getConfiguration().getRequestTimeout() > 0) { ChannelHandler handler = ctx.getPipeline().get("timeout"); if (handler != null) { LOG.trace("Removing timeout channel as we received message"); ctx.getPipeline().remove(handler); } } Exchange exchange = getExchange(ctx); if (exchange == null) { // we just ignore the received message as the channel is closed return; } AsyncCallback callback = getAsyncCallback(ctx); Message message; try { message = getResponseMessage(exchange, messageEvent); } catch (Exception e) { exchange.setException(e); callback.done(false); return; } // set the result on either IN or OUT on the original exchange depending on its pattern if (ExchangeHelper.isOutCapable(exchange)) { exchange.setOut(message); } else { exchange.setIn(message); } try { // should channel be closed after complete? Boolean close; if (ExchangeHelper.isOutCapable(exchange)) { close = exchange .getOut() .getHeader(NettyConstants.NETTY_CLOSE_CHANNEL_WHEN_COMPLETE, Boolean.class); } else { close = exchange .getIn() .getHeader(NettyConstants.NETTY_CLOSE_CHANNEL_WHEN_COMPLETE, Boolean.class); } // check the setting on the exchange property if (close == null) { close = exchange.getProperty(NettyConstants.NETTY_CLOSE_CHANNEL_WHEN_COMPLETE, Boolean.class); } // should we disconnect, the header can override the configuration boolean disconnect = producer.getConfiguration().isDisconnect(); if (close != null) { disconnect = close; } if (disconnect) { if (LOG.isTraceEnabled()) { LOG.trace( "Closing channel when complete at address: {}", producer.getConfiguration().getAddress()); } NettyHelper.close(ctx.getChannel()); } } finally { // signal callback callback.done(false); } }
/** * Enriches the input data (<code>exchange</code>) by first obtaining additional data from an * endpoint represented by an endpoint <code>producer</code> and second by aggregating input data * and additional data. Aggregation of input data and additional data is delegated to an {@link * org.apache.camel.processor.aggregate.AggregationStrategy} object set at construction time. If * the message exchange with the resource endpoint fails then no aggregation will be done and the * failed exchange content is copied over to the original message exchange. * * @param exchange input data. */ @Override public boolean process(Exchange exchange, AsyncCallback callback) { try { preCheckPoll(exchange); } catch (Exception e) { exchange.setException(new CamelExchangeException("Error during pre poll check", exchange, e)); callback.done(true); return true; } Exchange resourceExchange; if (timeout < 0) { LOG.debug("Consumer receive: {}", consumer); resourceExchange = consumer.receive(); } else if (timeout == 0) { LOG.debug("Consumer receiveNoWait: {}", consumer); resourceExchange = consumer.receiveNoWait(); } else { LOG.debug("Consumer receive with timeout: {} ms. {}", timeout, consumer); resourceExchange = consumer.receive(timeout); } if (resourceExchange == null) { LOG.debug("Consumer received no exchange"); } else { LOG.debug("Consumer received: {}", resourceExchange); } if (resourceExchange != null && resourceExchange.isFailed()) { // copy resource exchange onto original exchange (preserving pattern) copyResultsPreservePattern(exchange, resourceExchange); } else { prepareResult(exchange); try { // prepare the exchanges for aggregation ExchangeHelper.prepareAggregation(exchange, resourceExchange); // must catch any exception from aggregation Exchange aggregatedExchange = aggregationStrategy.aggregate(exchange, resourceExchange); if (aggregatedExchange != null) { // copy aggregation result onto original exchange (preserving pattern) copyResultsPreservePattern(exchange, aggregatedExchange); // handover any synchronization if (resourceExchange != null) { resourceExchange.handoverCompletions(exchange); } } } catch (Throwable e) { exchange.setException( new CamelExchangeException("Error occurred during aggregation", exchange, e)); callback.done(true); return true; } } // set header with the uri of the endpoint enriched so we can use that for tracing etc if (exchange.hasOut()) { exchange.getOut().setHeader(Exchange.TO_ENDPOINT, consumer.getEndpoint().getEndpointUri()); } else { exchange.getIn().setHeader(Exchange.TO_ENDPOINT, consumer.getEndpoint().getEndpointUri()); } callback.done(true); return true; }
@Override public void sendMessage( final Exchange exchange, final AsyncCallback callback, final MessageProducerResources producer) throws Exception { try { Collection<Message> messages = new ArrayList<Message>(1); if (exchange.getIn().getBody() != null) { if (exchange.getIn().getBody() instanceof List) { Iterable<?> payload = (Iterable<?>) exchange.getIn().getBody(); for (final Object object : payload) { Message message; if (BatchMessage.class.isInstance(object)) { BatchMessage<?> batchMessage = (BatchMessage<?>) object; message = getEndpoint() .getBinding() .makeJmsMessage( exchange, batchMessage.getPayload(), batchMessage.getHeaders(), producer.getSession(), null); } else { message = getEndpoint() .getBinding() .makeJmsMessage( exchange, object, exchange.getIn().getHeaders(), producer.getSession(), null); } messages.add(message); } } else { Message message = getEndpoint().getBinding().makeJmsMessage(exchange, producer.getSession()); messages.add(message); } } else { Message message = getEndpoint().getBinding().makeJmsMessage(exchange, producer.getSession()); messages.add(message); } if (isEndpointTransacted()) { exchange .getUnitOfWork() .addSynchronization( new SessionTransactionSynchronization( producer.getSession(), producer.getCommitStrategy())); } for (final Message message : messages) { producer.getMessageProducer().send(message); } } catch (Exception e) { exchange.setException(new Exception("Unable to complete sending the message: ", e)); } finally { if (producer != null) { getProducers().returnObject(producer); } callback.done(isSynchronous()); } }
@Override public String toString() { return delegate.toString(); }
/** * Common work which must be done when we are done multicasting. * * <p>This logic applies for both running synchronous and asynchronous as there are multiple exist * points when using the asynchronous routing engine. And therefore we want the logic in one * method instead of being scattered. * * @param original the original exchange * @param subExchange the current sub exchange, can be <tt>null</tt> for the synchronous part * @param pairs the pairs with the exchanges to process * @param callback the callback * @param doneSync the <tt>doneSync</tt> parameter to call on callback * @param forceExhaust whether or not error handling is exhausted */ protected void doDone( Exchange original, Exchange subExchange, final Iterable<ProcessorExchangePair> pairs, AsyncCallback callback, boolean doneSync, boolean forceExhaust) { // we are done so close the pairs iterator if (pairs != null && pairs instanceof Closeable) { IOHelper.close((Closeable) pairs, "pairs", LOG); } AggregationStrategy strategy = getAggregationStrategy(subExchange); // invoke the on completion callback if (strategy instanceof CompletionAwareAggregationStrategy) { ((CompletionAwareAggregationStrategy) strategy).onCompletion(subExchange); } // cleanup any per exchange aggregation strategy removeAggregationStrategyFromExchange(original); // we need to know if there was an exception, and if the stopOnException option was enabled // also we would need to know if any error handler has attempted redelivery and exhausted boolean stoppedOnException = false; boolean exception = false; boolean exhaust = forceExhaust || subExchange != null && (subExchange.getException() != null || ExchangeHelper.isRedeliveryExhausted(subExchange)); if (original.getException() != null || subExchange != null && subExchange.getException() != null) { // there was an exception and we stopped stoppedOnException = isStopOnException(); exception = true; } // must copy results at this point if (subExchange != null) { if (stoppedOnException) { // if we stopped due an exception then only propagte the exception original.setException(subExchange.getException()); } else { // copy the current result to original so it will contain this result of this eip ExchangeHelper.copyResults(original, subExchange); } } // .. and then if there was an exception we need to configure the redelivery exhaust // for example the noErrorHandler will not cause redelivery exhaust so if this error // handled has been in use, then the exhaust would be false (if not forced) if (exception) { // multicast uses error handling on its output processors and they have tried to redeliver // so we shall signal back to the other error handlers that we are exhausted and they should // not // also try to redeliver as we will then do that twice original.setProperty(Exchange.REDELIVERY_EXHAUSTED, exhaust); } callback.done(doneSync); }
/** * Enriches the input data (<code>exchange</code>) by first obtaining additional data from an * endpoint represented by an endpoint <code>producer</code> and second by aggregating input data * and additional data. Aggregation of input data and additional data is delegated to an {@link * org.apache.camel.processor.aggregate.AggregationStrategy} object set at construction time. If * the message exchange with the resource endpoint fails then no aggregation will be done and the * failed exchange content is copied over to the original message exchange. * * @param exchange input data. */ @Override public boolean process(Exchange exchange, AsyncCallback callback) { try { preCheckPoll(exchange); } catch (Exception e) { exchange.setException(new CamelExchangeException("Error during pre poll check", exchange, e)); callback.done(true); return true; } // which consumer to use PollingConsumer consumer; Endpoint endpoint; // use dynamic endpoint so calculate the endpoint to use Object recipient = null; try { recipient = expression.evaluate(exchange, Object.class); endpoint = resolveEndpoint(exchange, recipient); // acquire the consumer from the cache consumer = consumerCache.acquirePollingConsumer(endpoint); } catch (Throwable e) { if (isIgnoreInvalidEndpoint()) { if (LOG.isDebugEnabled()) { LOG.debug( "Endpoint uri is invalid: " + recipient + ". This exception will be ignored.", e); } } else { exchange.setException(e); } callback.done(true); return true; } Exchange resourceExchange; try { if (timeout < 0) { LOG.debug("Consumer receive: {}", consumer); resourceExchange = consumer.receive(); } else if (timeout == 0) { LOG.debug("Consumer receiveNoWait: {}", consumer); resourceExchange = consumer.receiveNoWait(); } else { LOG.debug("Consumer receive with timeout: {} ms. {}", timeout, consumer); resourceExchange = consumer.receive(timeout); } if (resourceExchange == null) { LOG.debug("Consumer received no exchange"); } else { LOG.debug("Consumer received: {}", resourceExchange); } } catch (Exception e) { exchange.setException(new CamelExchangeException("Error during poll", exchange, e)); callback.done(true); return true; } finally { // return the consumer back to the cache consumerCache.releasePollingConsumer(endpoint, consumer); } try { if (!isAggregateOnException() && (resourceExchange != null && resourceExchange.isFailed())) { // copy resource exchange onto original exchange (preserving pattern) copyResultsPreservePattern(exchange, resourceExchange); } else { prepareResult(exchange); // prepare the exchanges for aggregation ExchangeHelper.prepareAggregation(exchange, resourceExchange); // must catch any exception from aggregation Exchange aggregatedExchange = aggregationStrategy.aggregate(exchange, resourceExchange); if (aggregatedExchange != null) { // copy aggregation result onto original exchange (preserving pattern) copyResultsPreservePattern(exchange, aggregatedExchange); // handover any synchronization if (resourceExchange != null) { resourceExchange.handoverCompletions(exchange); } } } // set header with the uri of the endpoint enriched so we can use that for tracing etc if (exchange.hasOut()) { exchange.getOut().setHeader(Exchange.TO_ENDPOINT, consumer.getEndpoint().getEndpointUri()); } else { exchange.getIn().setHeader(Exchange.TO_ENDPOINT, consumer.getEndpoint().getEndpointUri()); } } catch (Throwable e) { exchange.setException( new CamelExchangeException("Error occurred during aggregation", exchange, e)); callback.done(true); return true; } callback.done(true); return true; }
/** * TODO time out is actually double as it waits for the producer and then waits for the response. * Use an atomic long to manage the countdown */ @Override public void sendMessage( final Exchange exchange, final AsyncCallback callback, final MessageProducerResources producer, final ReleaseProducerCallback releaseProducerCallback) throws Exception { Message request = getEndpoint().getBinding().makeJmsMessage(exchange, producer.getSession()); String correlationId = exchange.getIn().getHeader(JmsConstants.JMS_CORRELATION_ID, String.class); if (correlationId == null) { // we append the 'Camel-' prefix to know it was generated by us correlationId = GENERATED_CORRELATION_ID_PREFIX + getUuidGenerator().generateUuid(); } Object responseObject = null; Exchanger<Object> messageExchanger = new Exchanger<Object>(); JmsMessageHelper.setCorrelationId(request, correlationId); EXCHANGERS.put(correlationId, messageExchanger); MessageConsumerResources consumer = consumers.borrowObject(); JmsMessageHelper.setJMSReplyTo(request, consumer.getReplyToDestination()); consumers.returnObject(consumer); producer.getMessageProducer().send(request); // Return the producer to the pool so another waiting producer // can move forward // without waiting on us to complete the exchange try { releaseProducerCallback.release(producer); } catch (Exception exception) { // thrown if the pool is full. safe to ignore. } try { responseObject = messageExchanger.exchange(null, getResponseTimeOut(), TimeUnit.MILLISECONDS); EXCHANGERS.remove(correlationId); } catch (InterruptedException e) { log.debug("Exchanger was interrupted while waiting on response", e); exchange.setException(e); } catch (TimeoutException e) { log.debug("Exchanger timed out while waiting on response", e); exchange.setException(e); } if (exchange.getException() == null) { if (responseObject instanceof Throwable) { exchange.setException((Throwable) responseObject); } else if (responseObject instanceof Message) { Message message = (Message) responseObject; SjmsMessage response = new SjmsMessage(message, consumer.getSession(), getEndpoint().getBinding()); // the JmsBinding is designed to be "pull-based": it will populate the Camel message on // demand // therefore, we link Exchange and OUT message before continuing, so that the JmsBinding has // full access // to everything it may need, and can populate headers, properties, etc. accordingly (solves // CAMEL-6218). exchange.setOut(response); } else { exchange.setException(new CamelException("Unknown response type: " + responseObject)); } } callback.done(isSynchronous()); }
/** Process the exchange using redelivery error handling. */ protected boolean processErrorHandler( final Exchange exchange, final AsyncCallback callback, final RedeliveryData data) { // do a defensive copy of the original Exchange, which is needed for redelivery so we can ensure // the // original Exchange is being redelivered, and not a mutated Exchange data.original = defensiveCopyExchangeIfNeeded(exchange); // use looping to have redelivery attempts while (true) { // can we still run if (!isRunAllowed()) { if (exchange.getException() == null) { exchange.setException(new RejectedExecutionException()); } // we cannot process so invoke callback callback.done(data.sync); return data.sync; } // did previous processing cause an exception? boolean handle = shouldHandleException(exchange); if (handle) { handleException(exchange, data); } // compute if we are exhausted or not boolean exhausted = isExhausted(exchange, data); if (exhausted) { Processor target = null; boolean deliver = true; // the unit of work may have an optional callback associated we need to leverage SubUnitOfWorkCallback uowCallback = exchange.getUnitOfWork().getSubUnitOfWorkCallback(); if (uowCallback != null) { // signal to the callback we are exhausted uowCallback.onExhausted(exchange); // do not deliver to the failure processor as its been handled by the callback instead deliver = false; } if (deliver) { // should deliver to failure processor (either from onException or the dead letter // channel) target = data.failureProcessor != null ? data.failureProcessor : data.deadLetterProcessor; } // we should always invoke the deliverToFailureProcessor as it prepares, logs and does a // fair // bit of work for exhausted exchanges (its only the target processor which may be null if // handled by a savepoint) boolean sync = deliverToFailureProcessor(target, exchange, data, callback); // we are breaking out return sync; } if (data.redeliveryCounter > 0) { // calculate delay data.redeliveryDelay = data.currentRedeliveryPolicy.calculateRedeliveryDelay( data.redeliveryDelay, data.redeliveryCounter); if (data.redeliveryDelay > 0) { // okay there is a delay so create a scheduled task to have it executed in the future if (data.currentRedeliveryPolicy.isAsyncDelayedRedelivery() && !exchange.isTransacted()) { // let the RedeliverTask be the logic which tries to redeliver the Exchange which we can // used a scheduler to // have it being executed in the future, or immediately // we are continuing asynchronously // mark we are routing async from now and that this redelivery task came from a // synchronous routing data.sync = false; data.redeliverFromSync = true; AsyncRedeliveryTask task = new AsyncRedeliveryTask(exchange, callback, data); // schedule the redelivery task if (log.isTraceEnabled()) { log.trace( "Scheduling redelivery task to run in {} millis for exchangeId: {}", data.redeliveryDelay, exchange.getExchangeId()); } executorService.schedule(task, data.redeliveryDelay, TimeUnit.MILLISECONDS); return false; } else { // async delayed redelivery was disabled or we are transacted so we must be synchronous // as the transaction manager requires to execute in the same thread context try { data.currentRedeliveryPolicy.sleep(data.redeliveryDelay); } catch (InterruptedException e) { // we was interrupted so break out exchange.setException(e); // mark the exchange to stop continue routing when interrupted // as we do not want to continue routing (for example a task has been cancelled) exchange.setProperty(Exchange.ROUTE_STOP, Boolean.TRUE); callback.done(data.sync); return data.sync; } } } // prepare for redelivery prepareExchangeForRedelivery(exchange, data); // letting onRedeliver be executed deliverToOnRedeliveryProcessor(exchange, data); // emmit event we are doing redelivery EventHelper.notifyExchangeRedelivery( exchange.getContext(), exchange, data.redeliveryCounter); } // process the exchange (also redelivery) boolean sync = AsyncProcessorHelper.process( outputAsync, exchange, new AsyncCallback() { public void done(boolean sync) { // this callback should only handle the async case if (sync) { return; } // mark we are in async mode now data.sync = false; // if we are done then notify callback and exit if (isDone(exchange)) { callback.done(sync); return; } // error occurred so loop back around which we do by invoking the // processAsyncErrorHandler // method which takes care of this in a asynchronous manner processAsyncErrorHandler(exchange, callback, data); } }); if (!sync) { // the remainder of the Exchange is being processed asynchronously so we should return return false; } // we continue to route synchronously // if we are done then notify callback and exit boolean done = isDone(exchange); if (done) { callback.done(true); return true; } // error occurred so loop back around..... } }
/** All redelivery attempts failed so move the exchange to the dead letter queue */ protected boolean deliverToFailureProcessor( final Processor processor, final Exchange exchange, final RedeliveryData data, final AsyncCallback callback) { boolean sync = true; Exception caught = exchange.getException(); // we did not success with the redelivery so now we let the failure processor handle it // clear exception as we let the failure processor handle it exchange.setException(null); boolean handled = false; // regard both handled or continued as being handled if (shouldHandled(exchange, data) || shouldContinue(exchange, data)) { // its handled then remove traces of redelivery attempted exchange.getIn().removeHeader(Exchange.REDELIVERED); exchange.getIn().removeHeader(Exchange.REDELIVERY_COUNTER); exchange.getIn().removeHeader(Exchange.REDELIVERY_MAX_COUNTER); handled = true; } else { // must decrement the redelivery counter as we didn't process the redelivery but is // handling by the failure handler. So we must -1 to not let the counter be out-of-sync decrementRedeliveryCounter(exchange); } // is the a failure processor to process the Exchange if (processor != null) { // prepare original IN body if it should be moved instead of current body if (data.useOriginalInMessage) { log.trace("Using the original IN message instead of current"); Message original = exchange.getUnitOfWork().getOriginalInMessage(); exchange.setIn(original); if (exchange.hasOut()) { log.trace("Removing the out message to avoid some uncertain behavior"); exchange.setOut(null); } } // reset cached streams so they can be read again MessageHelper.resetStreamCache(exchange.getIn()); log.trace("Failure processor {} is processing Exchange: {}", processor, exchange); // store the last to endpoint as the failure endpoint exchange.setProperty(Exchange.FAILURE_ENDPOINT, exchange.getProperty(Exchange.TO_ENDPOINT)); // the failure processor could also be asynchronous AsyncProcessor afp = AsyncProcessorTypeConverter.convert(processor); sync = AsyncProcessorHelper.process( afp, exchange, new AsyncCallback() { public void done(boolean sync) { log.trace( "Failure processor done: {} processing Exchange: {}", processor, exchange); try { prepareExchangeAfterFailure(exchange, data); // fire event as we had a failure processor to handle it, which there is a event // for boolean deadLetterChannel = processor == data.deadLetterProcessor && data.deadLetterProcessor != null; EventHelper.notifyExchangeFailureHandled( exchange.getContext(), exchange, processor, deadLetterChannel); } finally { // if the fault was handled asynchronously, this should be reflected in the // callback as well data.sync &= sync; callback.done(data.sync); } } }); } else { try { // no processor but we need to prepare after failure as well prepareExchangeAfterFailure(exchange, data); } finally { // callback we are done callback.done(data.sync); } } // create log message String msg = "Failed delivery for exchangeId: " + exchange.getExchangeId(); msg = msg + ". Exhausted after delivery attempt: " + data.redeliveryCounter + " caught: " + caught; if (processor != null) { msg = msg + ". Processed by failure processor: " + processor; } // log that we failed delivery as we are exhausted logFailedDelivery(false, handled, false, exchange, msg, data, null); return sync; }
/** * This logic is only executed if we have to retry redelivery asynchronously, which have to be * done from the callback. * * <p>And therefore the logic is a bit different than the synchronous <tt>processErrorHandler</tt> * method which can use a loop based redelivery technique. However this means that these two * methods in overall have to be in <b>sync</b> in terms of logic. */ protected void processAsyncErrorHandler( final Exchange exchange, final AsyncCallback callback, final RedeliveryData data) { // can we still run if (!isRunAllowed()) { if (exchange.getException() == null) { exchange.setException(new RejectedExecutionException()); } callback.done(data.sync); return; } // did previous processing cause an exception? boolean handle = shouldHandleException(exchange); if (handle) { handleException(exchange, data); } // compute if we are exhausted or not boolean exhausted = isExhausted(exchange, data); if (exhausted) { Processor target = null; boolean deliver = true; // the unit of work may have an optional callback associated we need to leverage SubUnitOfWorkCallback uowCallback = exchange.getUnitOfWork().getSubUnitOfWorkCallback(); if (uowCallback != null) { // signal to the callback we are exhausted uowCallback.onExhausted(exchange); // do not deliver to the failure processor as its been handled by the callback instead deliver = false; } if (deliver) { // should deliver to failure processor (either from onException or the dead letter channel) target = data.failureProcessor != null ? data.failureProcessor : data.deadLetterProcessor; } // we should always invoke the deliverToFailureProcessor as it prepares, logs and does a fair // bit of work for exhausted exchanges (its only the target processor which may be null if // handled by a savepoint) deliverToFailureProcessor(target, exchange, data, callback); // we are breaking out return; } if (data.redeliveryCounter > 0) { // let the RedeliverTask be the logic which tries to redeliver the Exchange which we can used // a scheduler to // have it being executed in the future, or immediately // Note: the data.redeliverFromSync should be kept as is, in case it was enabled previously // to ensure the callback will continue routing from where we left AsyncRedeliveryTask task = new AsyncRedeliveryTask(exchange, callback, data); // calculate the redelivery delay data.redeliveryDelay = data.currentRedeliveryPolicy.calculateRedeliveryDelay( data.redeliveryDelay, data.redeliveryCounter); if (data.redeliveryDelay > 0) { // schedule the redelivery task if (log.isTraceEnabled()) { log.trace( "Scheduling redelivery task to run in {} millis for exchangeId: {}", data.redeliveryDelay, exchange.getExchangeId()); } executorService.schedule(task, data.redeliveryDelay, TimeUnit.MILLISECONDS); } else { // execute the task immediately executorService.submit(task); } } }
@Override public boolean process(final Exchange exchange, final AsyncCallback callback) { boolean done = false; try { switch (operationName) { case CREATE_JOB: JobInfo jobBody = exchange.getIn().getMandatoryBody(JobInfo.class); bulkClient.createJob( jobBody, new BulkApiClient.JobInfoResponseCallback() { @Override public void onResponse(JobInfo jobInfo, SalesforceException ex) { processResponse(exchange, jobInfo, ex, callback); } }); break; case GET_JOB: jobBody = exchange.getIn().getBody(JobInfo.class); String jobId; if (jobBody != null) { jobId = jobBody.getId(); } else { jobId = getParameter(JOB_ID, exchange, USE_BODY, NOT_OPTIONAL); } bulkClient.getJob( jobId, new BulkApiClient.JobInfoResponseCallback() { @Override public void onResponse(JobInfo jobInfo, SalesforceException ex) { processResponse(exchange, jobInfo, ex, callback); } }); break; case CLOSE_JOB: jobBody = exchange.getIn().getBody(JobInfo.class); if (jobBody != null) { jobId = jobBody.getId(); } else { jobId = getParameter(JOB_ID, exchange, USE_BODY, NOT_OPTIONAL); } bulkClient.closeJob( jobId, new BulkApiClient.JobInfoResponseCallback() { @Override public void onResponse(JobInfo jobInfo, SalesforceException ex) { processResponse(exchange, jobInfo, ex, callback); } }); break; case ABORT_JOB: jobBody = exchange.getIn().getBody(JobInfo.class); if (jobBody != null) { jobId = jobBody.getId(); } else { jobId = getParameter(JOB_ID, exchange, USE_BODY, NOT_OPTIONAL); } bulkClient.abortJob( jobId, new BulkApiClient.JobInfoResponseCallback() { @Override public void onResponse(JobInfo jobInfo, SalesforceException ex) { processResponse(exchange, jobInfo, ex, callback); } }); break; case CREATE_BATCH: // since request is in the body, use headers or endpoint params ContentType contentType = ContentType.fromValue( getParameter(CONTENT_TYPE, exchange, IGNORE_BODY, NOT_OPTIONAL)); jobId = getParameter(JOB_ID, exchange, IGNORE_BODY, NOT_OPTIONAL); InputStream request; try { request = exchange.getIn().getMandatoryBody(InputStream.class); } catch (CamelException e) { String msg = "Error preparing batch request: " + e.getMessage(); throw new SalesforceException(msg, e); } bulkClient.createBatch( request, jobId, contentType, new BulkApiClient.BatchInfoResponseCallback() { @Override public void onResponse(BatchInfo batchInfo, SalesforceException ex) { processResponse(exchange, batchInfo, ex, callback); } }); break; case GET_BATCH: BatchInfo batchBody = exchange.getIn().getBody(BatchInfo.class); String batchId; if (batchBody != null) { jobId = batchBody.getJobId(); batchId = batchBody.getId(); } else { jobId = getParameter(JOB_ID, exchange, IGNORE_BODY, NOT_OPTIONAL); batchId = getParameter(BATCH_ID, exchange, USE_BODY, NOT_OPTIONAL); } bulkClient.getBatch( jobId, batchId, new BulkApiClient.BatchInfoResponseCallback() { @Override public void onResponse(BatchInfo batchInfo, SalesforceException ex) { processResponse(exchange, batchInfo, ex, callback); } }); break; case GET_ALL_BATCHES: jobBody = exchange.getIn().getBody(JobInfo.class); if (jobBody != null) { jobId = jobBody.getId(); } else { jobId = getParameter(JOB_ID, exchange, USE_BODY, NOT_OPTIONAL); } bulkClient.getAllBatches( jobId, new BulkApiClient.BatchInfoListResponseCallback() { @Override public void onResponse(List<BatchInfo> batchInfoList, SalesforceException ex) { processResponse(exchange, batchInfoList, ex, callback); } }); break; case GET_REQUEST: batchBody = exchange.getIn().getBody(BatchInfo.class); if (batchBody != null) { jobId = batchBody.getJobId(); batchId = batchBody.getId(); } else { jobId = getParameter(JOB_ID, exchange, IGNORE_BODY, NOT_OPTIONAL); batchId = getParameter(BATCH_ID, exchange, USE_BODY, NOT_OPTIONAL); } bulkClient.getRequest( jobId, batchId, new BulkApiClient.StreamResponseCallback() { @Override public void onResponse(InputStream inputStream, SalesforceException ex) { // read the request stream into a StreamCache temp file // ensures the connection is read StreamCache body = null; if (inputStream != null) { try { body = StreamCacheConverter.convertToStreamCache(inputStream, exchange); } catch (IOException e) { String msg = "Error retrieving batch request: " + e.getMessage(); ex = new SalesforceException(msg, e); } finally { // close the input stream to release the Http connection try { inputStream.close(); } catch (IOException ignore) { } } } processResponse(exchange, body, ex, callback); } }); break; case GET_RESULTS: batchBody = exchange.getIn().getBody(BatchInfo.class); if (batchBody != null) { jobId = batchBody.getJobId(); batchId = batchBody.getId(); } else { jobId = getParameter(JOB_ID, exchange, IGNORE_BODY, NOT_OPTIONAL); batchId = getParameter(BATCH_ID, exchange, USE_BODY, NOT_OPTIONAL); } bulkClient.getResults( jobId, batchId, new BulkApiClient.StreamResponseCallback() { @Override public void onResponse(InputStream inputStream, SalesforceException ex) { // read the result stream into a StreamCache temp file // ensures the connection is read StreamCache body = null; if (inputStream != null) { try { body = StreamCacheConverter.convertToStreamCache(inputStream, exchange); } catch (IOException e) { String msg = "Error retrieving batch results: " + e.getMessage(); ex = new SalesforceException(msg, e); } finally { // close the input stream to release the Http connection try { inputStream.close(); } catch (IOException ignore) { } } } processResponse(exchange, body, ex, callback); } }); break; case CREATE_BATCH_QUERY: jobBody = exchange.getIn().getBody(JobInfo.class); String soqlQuery; if (jobBody != null) { jobId = jobBody.getId(); contentType = jobBody.getContentType(); // use SOQL query from header or endpoint config soqlQuery = getParameter(SOBJECT_QUERY, exchange, IGNORE_BODY, NOT_OPTIONAL); } else { jobId = getParameter(JOB_ID, exchange, IGNORE_BODY, NOT_OPTIONAL); contentType = ContentType.fromValue( getParameter(CONTENT_TYPE, exchange, IGNORE_BODY, NOT_OPTIONAL)); // reuse SOBJECT_QUERY property soqlQuery = getParameter(SOBJECT_QUERY, exchange, USE_BODY, NOT_OPTIONAL); } bulkClient.createBatchQuery( jobId, soqlQuery, contentType, new BulkApiClient.BatchInfoResponseCallback() { @Override public void onResponse(BatchInfo batchInfo, SalesforceException ex) { processResponse(exchange, batchInfo, ex, callback); } }); break; case GET_QUERY_RESULT_IDS: batchBody = exchange.getIn().getBody(BatchInfo.class); if (batchBody != null) { jobId = batchBody.getJobId(); batchId = batchBody.getId(); } else { jobId = getParameter(JOB_ID, exchange, IGNORE_BODY, NOT_OPTIONAL); batchId = getParameter(BATCH_ID, exchange, USE_BODY, NOT_OPTIONAL); } bulkClient.getQueryResultIds( jobId, batchId, new BulkApiClient.QueryResultIdsCallback() { @Override public void onResponse(List<String> ids, SalesforceException ex) { processResponse(exchange, ids, ex, callback); } }); break; case GET_QUERY_RESULT: batchBody = exchange.getIn().getBody(BatchInfo.class); String resultId; if (batchBody != null) { jobId = batchBody.getJobId(); batchId = batchBody.getId(); resultId = getParameter(RESULT_ID, exchange, IGNORE_BODY, NOT_OPTIONAL); } else { jobId = getParameter(JOB_ID, exchange, IGNORE_BODY, NOT_OPTIONAL); batchId = getParameter(BATCH_ID, exchange, IGNORE_BODY, NOT_OPTIONAL); resultId = getParameter(RESULT_ID, exchange, USE_BODY, NOT_OPTIONAL); } bulkClient.getQueryResult( jobId, batchId, resultId, new BulkApiClient.StreamResponseCallback() { @Override public void onResponse(InputStream inputStream, SalesforceException ex) { StreamCache body = null; if (inputStream != null) { // read the result stream into a StreamCache temp file // ensures the connection is read try { body = StreamCacheConverter.convertToStreamCache(inputStream, exchange); } catch (IOException e) { String msg = "Error retrieving query result: " + e.getMessage(); ex = new SalesforceException(msg, e); } finally { // close the input stream to release the Http connection try { inputStream.close(); } catch (IOException e) { // ignore } } } processResponse(exchange, body, ex, callback); } }); break; default: throw new SalesforceException("Unknow operation name: " + operationName, null); } } catch (SalesforceException e) { exchange.setException( new SalesforceException( String.format( "Error processing %s: [%s] \"%s\"", operationName, e.getStatusCode(), e.getMessage()), e)); callback.done(true); done = true; } catch (InvalidPayloadException e) { exchange.setException( new SalesforceException( String.format( "Unexpected Error processing %s: \"%s\"", operationName, e.getMessage()), e)); callback.done(true); done = true; } catch (RuntimeException e) { exchange.setException( new SalesforceException( String.format( "Unexpected Error processing %s: \"%s\"", operationName, e.getMessage()), e)); callback.done(true); done = true; } // continue routing asynchronously if false return done; }
@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; } }