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; 018 019import java.util.Iterator; 020import java.util.concurrent.ExecutorService; 021 022import org.apache.camel.AsyncCallback; 023import org.apache.camel.AsyncProcessor; 024import org.apache.camel.CamelContext; 025import org.apache.camel.Endpoint; 026import org.apache.camel.Exchange; 027import org.apache.camel.Expression; 028import org.apache.camel.Processor; 029import org.apache.camel.impl.EmptyProducerCache; 030import org.apache.camel.impl.ProducerCache; 031import org.apache.camel.processor.aggregate.AggregationStrategy; 032import org.apache.camel.processor.aggregate.UseLatestAggregationStrategy; 033import org.apache.camel.support.ServiceSupport; 034import org.apache.camel.util.AsyncProcessorHelper; 035import org.apache.camel.util.ExchangeHelper; 036import org.apache.camel.util.ObjectHelper; 037import org.apache.camel.util.ServiceHelper; 038import org.slf4j.Logger; 039import org.slf4j.LoggerFactory; 040 041import static org.apache.camel.util.ObjectHelper.notNull; 042 043/** 044 * Implements a dynamic <a 045 * href="http://camel.apache.org/recipient-list.html">Recipient List</a> 046 * pattern where the list of actual endpoints to send a message exchange to are 047 * dependent on some dynamic expression. 048 * 049 * @version 050 */ 051public class RecipientList extends ServiceSupport implements AsyncProcessor { 052 053 private static final Logger LOG = LoggerFactory.getLogger(RecipientList.class); 054 private static final String IGNORE_DELIMITER_MARKER = "false"; 055 private final CamelContext camelContext; 056 private ProducerCache producerCache; 057 private Expression expression; 058 private final String delimiter; 059 private boolean parallelProcessing; 060 private boolean parallelAggregate; 061 private boolean stopOnException; 062 private boolean ignoreInvalidEndpoints; 063 private boolean streaming; 064 private long timeout; 065 private int cacheSize; 066 private Processor onPrepare; 067 private boolean shareUnitOfWork; 068 private ExecutorService executorService; 069 private boolean shutdownExecutorService; 070 private ExecutorService aggregateExecutorService; 071 private AggregationStrategy aggregationStrategy = new UseLatestAggregationStrategy(); 072 073 public RecipientList(CamelContext camelContext) { 074 // use comma by default as delimiter 075 this(camelContext, ","); 076 } 077 078 public RecipientList(CamelContext camelContext, String delimiter) { 079 notNull(camelContext, "camelContext"); 080 ObjectHelper.notEmpty(delimiter, "delimiter"); 081 this.camelContext = camelContext; 082 this.delimiter = delimiter; 083 } 084 085 public RecipientList(CamelContext camelContext, Expression expression) { 086 // use comma by default as delimiter 087 this(camelContext, expression, ","); 088 } 089 090 public RecipientList(CamelContext camelContext, Expression expression, String delimiter) { 091 notNull(camelContext, "camelContext"); 092 ObjectHelper.notNull(expression, "expression"); 093 ObjectHelper.notEmpty(delimiter, "delimiter"); 094 this.camelContext = camelContext; 095 this.expression = expression; 096 this.delimiter = delimiter; 097 } 098 099 @Override 100 public String toString() { 101 return "RecipientList[" + (expression != null ? expression : "") + "]"; 102 } 103 104 public void process(Exchange exchange) throws Exception { 105 AsyncProcessorHelper.process(this, exchange); 106 } 107 108 public boolean process(Exchange exchange, AsyncCallback callback) { 109 if (!isStarted()) { 110 throw new IllegalStateException("RecipientList has not been started: " + this); 111 } 112 113 // use the evaluate expression result if exists 114 Object recipientList = exchange.removeProperty(Exchange.EVALUATE_EXPRESSION_RESULT); 115 if (recipientList == null && expression != null) { 116 // fallback and evaluate the expression 117 recipientList = expression.evaluate(exchange, Object.class); 118 } 119 120 return sendToRecipientList(exchange, recipientList, callback); 121 } 122 123 /** 124 * Sends the given exchange to the recipient list 125 */ 126 public boolean sendToRecipientList(Exchange exchange, Object recipientList, AsyncCallback callback) { 127 Iterator<Object> iter; 128 129 if (delimiter != null && delimiter.equalsIgnoreCase(IGNORE_DELIMITER_MARKER)) { 130 iter = ObjectHelper.createIterator(recipientList, null); 131 } else { 132 iter = ObjectHelper.createIterator(recipientList, delimiter); 133 } 134 135 RecipientListProcessor rlp = new RecipientListProcessor(exchange.getContext(), producerCache, iter, getAggregationStrategy(), 136 isParallelProcessing(), getExecutorService(), isShutdownExecutorService(), 137 isStreaming(), isStopOnException(), getTimeout(), getOnPrepare(), isShareUnitOfWork(), isParallelAggregate()) { 138 @Override 139 protected synchronized ExecutorService createAggregateExecutorService(String name) { 140 // use a shared executor service to avoid creating new thread pools 141 if (aggregateExecutorService == null) { 142 aggregateExecutorService = super.createAggregateExecutorService("RecipientList-AggregateTask"); 143 } 144 return aggregateExecutorService; 145 } 146 }; 147 rlp.setIgnoreInvalidEndpoints(isIgnoreInvalidEndpoints()); 148 149 // start the service 150 try { 151 ServiceHelper.startService(rlp); 152 } catch (Exception e) { 153 exchange.setException(e); 154 callback.done(true); 155 return true; 156 } 157 158 AsyncProcessor target = rlp; 159 if (isShareUnitOfWork()) { 160 // wrap answer in a sub unit of work, since we share the unit of work 161 CamelInternalProcessor internalProcessor = new CamelInternalProcessor(rlp); 162 internalProcessor.addAdvice(new CamelInternalProcessor.SubUnitOfWorkProcessorAdvice()); 163 target = internalProcessor; 164 } 165 166 // now let the multicast process the exchange 167 return target.process(exchange, callback); 168 } 169 170 protected Endpoint resolveEndpoint(Exchange exchange, Object recipient) { 171 // trim strings as end users might have added spaces between separators 172 if (recipient instanceof String) { 173 recipient = ((String)recipient).trim(); 174 } 175 return ExchangeHelper.resolveEndpoint(exchange, recipient); 176 } 177 178 protected void doStart() throws Exception { 179 if (producerCache == null) { 180 if (cacheSize < 0) { 181 producerCache = new EmptyProducerCache(this, camelContext); 182 LOG.debug("RecipientList {} is not using ProducerCache", this); 183 } else if (cacheSize == 0) { 184 producerCache = new ProducerCache(this, camelContext); 185 LOG.debug("RecipientList {} using ProducerCache with default cache size", this); 186 } else { 187 producerCache = new ProducerCache(this, camelContext, cacheSize); 188 LOG.debug("RecipientList {} using ProducerCache with cacheSize={}", this, cacheSize); 189 } 190 } 191 ServiceHelper.startServices(aggregationStrategy, producerCache); 192 } 193 194 protected void doStop() throws Exception { 195 ServiceHelper.stopServices(producerCache, aggregationStrategy); 196 } 197 198 protected void doShutdown() throws Exception { 199 ServiceHelper.stopAndShutdownServices(producerCache, aggregationStrategy); 200 201 if (shutdownExecutorService && executorService != null) { 202 camelContext.getExecutorServiceManager().shutdownNow(executorService); 203 } 204 } 205 206 public boolean isStreaming() { 207 return streaming; 208 } 209 210 public void setStreaming(boolean streaming) { 211 this.streaming = streaming; 212 } 213 214 public boolean isIgnoreInvalidEndpoints() { 215 return ignoreInvalidEndpoints; 216 } 217 218 public void setIgnoreInvalidEndpoints(boolean ignoreInvalidEndpoints) { 219 this.ignoreInvalidEndpoints = ignoreInvalidEndpoints; 220 } 221 222 public boolean isParallelProcessing() { 223 return parallelProcessing; 224 } 225 226 public void setParallelProcessing(boolean parallelProcessing) { 227 this.parallelProcessing = parallelProcessing; 228 } 229 230 public boolean isParallelAggregate() { 231 return parallelAggregate; 232 } 233 234 public void setParallelAggregate(boolean parallelAggregate) { 235 this.parallelAggregate = parallelAggregate; 236 } 237 238 public boolean isStopOnException() { 239 return stopOnException; 240 } 241 242 public void setStopOnException(boolean stopOnException) { 243 this.stopOnException = stopOnException; 244 } 245 246 public ExecutorService getExecutorService() { 247 return executorService; 248 } 249 250 public void setExecutorService(ExecutorService executorService) { 251 this.executorService = executorService; 252 } 253 254 public boolean isShutdownExecutorService() { 255 return shutdownExecutorService; 256 } 257 258 public void setShutdownExecutorService(boolean shutdownExecutorService) { 259 this.shutdownExecutorService = shutdownExecutorService; 260 } 261 262 public AggregationStrategy getAggregationStrategy() { 263 return aggregationStrategy; 264 } 265 266 public void setAggregationStrategy(AggregationStrategy aggregationStrategy) { 267 this.aggregationStrategy = aggregationStrategy; 268 } 269 270 public long getTimeout() { 271 return timeout; 272 } 273 274 public void setTimeout(long timeout) { 275 this.timeout = timeout; 276 } 277 278 public Processor getOnPrepare() { 279 return onPrepare; 280 } 281 282 public void setOnPrepare(Processor onPrepare) { 283 this.onPrepare = onPrepare; 284 } 285 286 public boolean isShareUnitOfWork() { 287 return shareUnitOfWork; 288 } 289 290 public void setShareUnitOfWork(boolean shareUnitOfWork) { 291 this.shareUnitOfWork = shareUnitOfWork; 292 } 293 294 public int getCacheSize() { 295 return cacheSize; 296 } 297 298 public void setCacheSize(int cacheSize) { 299 this.cacheSize = cacheSize; 300 } 301}