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