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;
018    
019    import org.apache.camel.Endpoint;
020    import org.apache.camel.Exchange;
021    import org.apache.camel.Message;
022    import org.apache.camel.Processor;
023    import org.apache.camel.Producer;
024    import org.apache.camel.impl.ProducerCache;
025    import org.apache.camel.impl.ServiceSupport;
026    import org.apache.camel.model.RoutingSlipDefinition;
027    import org.apache.camel.util.ExchangeHelper;
028    import org.apache.commons.logging.Log;
029    import org.apache.commons.logging.LogFactory;
030    import static org.apache.camel.util.ObjectHelper.notNull;
031    
032    /**
033     * Implements a <a href="http://camel.apache.org/routing-slip.html">Routing Slip</a>
034     * pattern where the list of actual endpoints to send a message exchange to are
035     * dependent on the value of a message header.
036     */
037    public class RoutingSlip extends ServiceSupport implements Processor {
038        private static final transient Log LOG = LogFactory.getLog(RoutingSlip.class);
039        private final String header;
040        private final String uriDelimiter;
041    
042        private ProducerCache producerCache = new ProducerCache();
043    
044        public RoutingSlip(String header) {
045            this(header, RoutingSlipDefinition.DEFAULT_DELIMITER);
046        }
047    
048        public RoutingSlip(String header, String uriDelimiter) {
049            notNull(header, "header");
050            notNull(uriDelimiter, "uriDelimiter");
051    
052            this.header = header;
053            this.uriDelimiter = uriDelimiter;
054        }
055    
056        @Override
057        public String toString() {
058            return "RoutingSlip[header=" + header + " uriDelimiter=" + uriDelimiter + "]";
059        }
060    
061        public void process(Exchange exchange) throws Exception {
062            Message message = exchange.getIn();
063            String[] recipients = recipients(message);
064            Exchange current = exchange;
065    
066            for (String nextRecipient : recipients) {
067                Endpoint endpoint = resolveEndpoint(exchange, nextRecipient);
068                Producer producer = producerCache.getProducer(endpoint);
069                Exchange ex = current.newInstance();
070    
071                updateRoutingSlip(current);
072                copyOutToIn(ex, current);
073    
074                producer.process(ex);
075    
076                current = ex;
077            }
078            ExchangeHelper.copyResults(exchange, current);
079        }
080    
081        protected Endpoint resolveEndpoint(Exchange exchange, Object recipient) {
082            return ExchangeHelper.resolveEndpoint(exchange, recipient);
083        }
084    
085        protected void doStop() throws Exception {
086            producerCache.stop();
087        }
088    
089        protected void doStart() throws Exception {
090        }
091    
092        private void updateRoutingSlip(Exchange current) {
093            Message message = getResultMessage(current);
094            String oldSlip = message.getHeader(header, String.class);
095            if (oldSlip != null) {
096                int delimiterIndex = oldSlip.indexOf(uriDelimiter);
097                String newSlip = delimiterIndex > 0 ? oldSlip.substring(delimiterIndex + 1) : "";
098                message.setHeader(header, newSlip);
099            }
100        }
101    
102        /**
103         * Returns the outbound message if available. Otherwise return the inbound
104         * message.
105         */
106        private Message getResultMessage(Exchange exchange) {
107            Message message = exchange.getOut(false);
108            // if this endpoint had no out (like a mock endpoint)
109            // just take the in
110            if (message == null) {
111                message = exchange.getIn();
112            }
113            return message;
114        }
115    
116        /**
117         * Return the list of recipients defined in the routing slip in the
118         * specified message.
119         */
120        private String[] recipients(Message message) {
121            Object headerValue = message.getHeader(header);
122            if (headerValue != null && !headerValue.equals("")) {
123                return headerValue.toString().split(uriDelimiter);
124            }
125            return new String[] {};
126        }
127    
128        /**
129         * Copy the outbound data in 'source' to the inbound data in 'result'.
130         */
131        private void copyOutToIn(Exchange result, Exchange source) {
132            result.setException(source.getException());
133    
134            Message fault = source.getFault(false);
135            if (fault != null) {
136                result.getFault(true).copyFrom(fault);
137            }
138    
139            result.setIn(getResultMessage(source));
140    
141            result.getProperties().clear();
142            result.getProperties().putAll(source.getProperties());
143        }
144    }