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 */
017 package org.apache.camel.processor.idempotent;
018
019 import org.apache.camel.Exchange;
020 import org.apache.camel.Expression;
021 import org.apache.camel.Processor;
022 import org.apache.camel.impl.ServiceSupport;
023 import org.apache.camel.spi.IdempotentRepository;
024 import org.apache.camel.util.ServiceHelper;
025 import org.apache.commons.logging.Log;
026 import org.apache.commons.logging.LogFactory;
027
028 /**
029 * An implementation of the <a
030 * href="http://camel.apache.org/idempotent-consumer.html">Idempotent
031 * Consumer</a> pattern.
032 *
033 * @version $Revision: 752775 $
034 */
035 public class IdempotentConsumer extends ServiceSupport implements Processor {
036 private static final transient Log LOG = LogFactory.getLog(IdempotentConsumer.class);
037 private final Expression messageIdExpression;
038 private final Processor nextProcessor;
039 private final IdempotentRepository idempotentRepository;
040
041 public IdempotentConsumer(Expression messageIdExpression, IdempotentRepository idempotentRepository,
042 Processor nextProcessor) {
043 this.messageIdExpression = messageIdExpression;
044 this.idempotentRepository = idempotentRepository;
045 this.nextProcessor = nextProcessor;
046 }
047
048 @Override
049 public String toString() {
050 return "IdempotentConsumer[expression=" + messageIdExpression + ", repository=" + idempotentRepository
051 + ", processor=" + nextProcessor + "]";
052 }
053
054 @SuppressWarnings("unchecked")
055 public void process(Exchange exchange) throws Exception {
056 String messageId = messageIdExpression.evaluate(exchange, String.class);
057 if (messageId == null) {
058 throw new NoMessageIdException(exchange, messageIdExpression);
059 }
060
061 if (idempotentRepository.contains(messageId)) {
062 onDuplicateMessage(exchange, messageId);
063 } else {
064 // process it first
065 nextProcessor.process(exchange);
066
067 // then test wheter it was failed or not
068 if (!exchange.isFailed()) {
069 onCompletedMessage(exchange, messageId);
070 } else {
071 onFailedMessage(exchange, messageId);
072 }
073 }
074 }
075
076 // Properties
077 // -------------------------------------------------------------------------
078 public Expression getMessageIdExpression() {
079 return messageIdExpression;
080 }
081
082 public IdempotentRepository getIdempotentRepository() {
083 return idempotentRepository;
084 }
085
086 public Processor getNextProcessor() {
087 return nextProcessor;
088 }
089
090 // Implementation methods
091 // -------------------------------------------------------------------------
092
093 protected void doStart() throws Exception {
094 ServiceHelper.startServices(nextProcessor);
095 }
096
097 protected void doStop() throws Exception {
098 ServiceHelper.stopServices(nextProcessor);
099 }
100
101 /**
102 * A strategy method to allow derived classes to overload the behaviour of
103 * processing a duplicate message
104 *
105 * @param exchange the exchange
106 * @param messageId the message ID of this exchange
107 */
108 protected void onDuplicateMessage(Exchange exchange, String messageId) {
109 if (LOG.isDebugEnabled()) {
110 LOG.debug("Ignoring duplicate message with id: " + messageId + " for exchange: " + exchange);
111 }
112 }
113
114 /**
115 * A strategy method to allow derived classes to overload the behaviour of
116 * processing a completed message
117 *
118 * @param exchange the exchange
119 * @param messageId the message ID of this exchange
120 */
121 @SuppressWarnings("unchecked")
122 protected void onCompletedMessage(Exchange exchange, String messageId) {
123 idempotentRepository.add(messageId);
124 if (LOG.isDebugEnabled()) {
125 LOG.debug("Added to repository with id: " + messageId + " for exchange: " + exchange);
126 }
127 }
128
129 /**
130 * A strategy method to allow derived classes to overload the behaviour of
131 * processing a failed message
132 *
133 * @param exchange the exchange
134 * @param messageId the message ID of this exchange
135 */
136 protected void onFailedMessage(Exchange exchange, String messageId) {
137 if (LOG.isDebugEnabled()) {
138 LOG.debug("Not added to repository as exchange failed: " + exchange + " with id: " + messageId);
139 }
140 }
141 }