Esempio n. 1
0
  @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);
    }
  }
Esempio n. 4
0
    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;
  }
Esempio n. 6
0
  @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;
  }
Esempio n. 7
0
  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);
    }
  }
Esempio n. 10
0
  /**
   * 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;
  }
Esempio n. 11
0
  @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());
    }
  }
Esempio n. 12
0
 @Override
 public String toString() {
   return delegate.toString();
 }
Esempio n. 13
0
  /**
   * 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);
  }
Esempio n. 14
0
  /**
   * 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;
  }
Esempio n. 15
0
  /**
   * 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);
      }
    }
  }
Esempio n. 19
0
  @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;
    }
  }