001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.processor.idempotent;
018
019import org.apache.camel.Exchange;
020import org.apache.camel.spi.ExchangeIdempotentRepository;
021import org.apache.camel.spi.IdempotentRepository;
022import org.apache.camel.spi.Synchronization;
023import org.apache.camel.util.ExchangeHelper;
024import org.slf4j.Logger;
025import org.slf4j.LoggerFactory;
026
027/**
028 * On completion strategy for {@link org.apache.camel.processor.idempotent.IdempotentConsumer}.
029 * <p/>
030 * This strategy adds the message id to the idempotent repository in cast the exchange
031 * was processed successfully. In case of failure the message id is <b>not</b> added.
032 */
033public class IdempotentOnCompletion implements Synchronization {
034    private static final Logger LOG = LoggerFactory.getLogger(IdempotentOnCompletion.class);
035    private final IdempotentRepository<String> idempotentRepository;
036    private final String messageId;
037    private final boolean eager;
038    private final boolean removeOnFailure;
039
040    public IdempotentOnCompletion(IdempotentRepository<String> idempotentRepository, String messageId, boolean eager, boolean removeOnFailure) {
041        this.idempotentRepository = idempotentRepository;
042        this.messageId = messageId;
043        this.eager = eager;
044        this.removeOnFailure = removeOnFailure;
045    }
046
047    public void onComplete(Exchange exchange) {
048        if (ExchangeHelper.isFailureHandled(exchange)) {
049            // the exchange did not process successfully but was failure handled by the dead letter channel
050            // and thus moved to the dead letter queue. We should thus not consider it as complete.
051            onFailedMessage(exchange, messageId);
052        } else {
053            onCompletedMessage(exchange, messageId);
054        }
055    }
056
057    public void onFailure(Exchange exchange) {
058        onFailedMessage(exchange, messageId);
059    }
060
061    /**
062     * A strategy method to allow derived classes to overload the behavior of
063     * processing a completed message
064     *
065     * @param exchange  the exchange
066     * @param messageId the message ID of this exchange
067     */
068    protected void onCompletedMessage(Exchange exchange, String messageId) {
069        if (!eager) {
070            // if not eager we should add the key when its complete
071            if (idempotentRepository instanceof ExchangeIdempotentRepository) {
072                ((ExchangeIdempotentRepository<String>) idempotentRepository).add(exchange, messageId);
073            } else {
074                idempotentRepository.add(messageId);
075            }
076        }
077        if (idempotentRepository instanceof ExchangeIdempotentRepository) {
078            ((ExchangeIdempotentRepository<String>) idempotentRepository).confirm(exchange, messageId);
079        } else {
080            idempotentRepository.confirm(messageId);
081        }
082    }
083
084    /**
085     * A strategy method to allow derived classes to overload the behavior of
086     * processing a failed message
087     *
088     * @param exchange  the exchange
089     * @param messageId the message ID of this exchange
090     */
091    protected void onFailedMessage(Exchange exchange, String messageId) {
092        if (removeOnFailure) {
093            if (idempotentRepository instanceof ExchangeIdempotentRepository) {
094                ((ExchangeIdempotentRepository<String>) idempotentRepository).remove(exchange, messageId);
095            } else {
096                idempotentRepository.remove(messageId);
097            }
098            LOG.debug("Removed from repository as exchange failed: {} with id: {}", exchange, messageId);
099        }
100    }
101
102    @Override
103    public String toString() {
104        return "IdempotentOnCompletion[" + messageId + ']';
105    }
106}