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.component.seda; 018 019import java.util.HashMap; 020import java.util.Map; 021import java.util.concurrent.BlockingQueue; 022 023import org.apache.camel.Component; 024import org.apache.camel.Endpoint; 025import org.apache.camel.Exchange; 026import org.apache.camel.impl.UriEndpointComponent; 027import org.slf4j.Logger; 028import org.slf4j.LoggerFactory; 029 030/** 031 * The <a href="http://camel.apache.org/seda.html">SEDA Component</a> is for asynchronous SEDA exchanges on a {@link BlockingQueue} within a CamelContext 032 * 033 * @version 034 */ 035public class SedaComponent extends UriEndpointComponent { 036 protected final Logger log = LoggerFactory.getLogger(getClass()); 037 protected final int maxConcurrentConsumers = 500; 038 protected int queueSize; 039 protected int concurrentConsumers = 1; 040 private final Map<String, QueueReference> queues = new HashMap<String, QueueReference>(); 041 private BlockingQueueFactory<Exchange> defaultQueueFactory = new LinkedBlockingQueueFactory<Exchange>(); 042 043 public SedaComponent() { 044 super(SedaEndpoint.class); 045 } 046 047 public SedaComponent(Class<? extends Endpoint> endpointClass) { 048 super(endpointClass); 049 } 050 051 /** 052 * Sets the default maximum capacity of the SEDA queue (i.e., the number of messages it can hold). 053 */ 054 public void setQueueSize(int size) { 055 queueSize = size; 056 } 057 058 public int getQueueSize() { 059 return queueSize; 060 } 061 062 /** 063 * Sets the default number of concurrent threads processing exchanges. 064 */ 065 public void setConcurrentConsumers(int size) { 066 concurrentConsumers = size; 067 } 068 069 public int getConcurrentConsumers() { 070 return concurrentConsumers; 071 } 072 073 public BlockingQueueFactory<Exchange> getDefaultQueueFactory() { 074 return defaultQueueFactory; 075 } 076 077 /** 078 * Sets the default queue factory. 079 */ 080 public void setDefaultQueueFactory(BlockingQueueFactory<Exchange> defaultQueueFactory) { 081 this.defaultQueueFactory = defaultQueueFactory; 082 } 083 084 /** 085 * @deprecated use 086 */ 087 @Deprecated 088 public synchronized QueueReference getOrCreateQueue(SedaEndpoint endpoint, Integer size) { 089 return getOrCreateQueue(endpoint, size, null); 090 } 091 092 /** 093 * @deprecated use {@link #getOrCreateQueue(SedaEndpoint, Integer, Boolean, BlockingQueueFactory)} 094 */ 095 public synchronized QueueReference getOrCreateQueue(SedaEndpoint endpoint, Integer size, Boolean multipleConsumers) { 096 return getOrCreateQueue(endpoint, size, multipleConsumers, null); 097 } 098 099 public synchronized QueueReference getOrCreateQueue(SedaEndpoint endpoint, Integer size, Boolean multipleConsumers, BlockingQueueFactory<Exchange> customQueueFactory) { 100 String key = getQueueKey(endpoint.getEndpointUri()); 101 102 QueueReference ref = getQueues().get(key); 103 if (ref != null) { 104 105 // if the given size is not provided, we just use the existing queue as is 106 if (size != null && !size.equals(ref.getSize())) { 107 // there is already a queue, so make sure the size matches 108 throw new IllegalArgumentException("Cannot use existing queue " + key + " as the existing queue size " 109 + (ref.getSize() != null ? ref.getSize() : Integer.MAX_VALUE) + " does not match given queue size " + size); 110 } 111 // add the reference before returning queue 112 ref.addReference(endpoint); 113 114 if (log.isDebugEnabled()) { 115 log.debug("Reusing existing queue {} with size {} and reference count {}", new Object[]{key, size, ref.getCount()}); 116 } 117 return ref; 118 } 119 120 // create queue 121 BlockingQueue<Exchange> queue; 122 BlockingQueueFactory<Exchange> queueFactory = customQueueFactory == null ? defaultQueueFactory : customQueueFactory; 123 if (size != null && size > 0) { 124 queue = queueFactory.create(size); 125 } else { 126 if (getQueueSize() > 0) { 127 size = getQueueSize(); 128 queue = queueFactory.create(getQueueSize()); 129 } else { 130 queue = queueFactory.create(); 131 } 132 } 133 log.debug("Created queue {} with size {}", key, size); 134 135 // create and add a new reference queue 136 ref = new QueueReference(queue, size, multipleConsumers); 137 ref.addReference(endpoint); 138 getQueues().put(key, ref); 139 140 return ref; 141 } 142 143 public synchronized QueueReference registerQueue(SedaEndpoint endpoint, BlockingQueue<Exchange> queue) { 144 String key = getQueueKey(endpoint.getEndpointUri()); 145 146 QueueReference ref = getQueues().get(key); 147 if (ref == null) { 148 ref = new QueueReference(queue, endpoint.getSize(), endpoint.isMultipleConsumers()); 149 ref.addReference(endpoint); 150 getQueues().put(key, ref); 151 } 152 153 return ref; 154 } 155 156 public Map<String, QueueReference> getQueues() { 157 return queues; 158 } 159 160 public QueueReference getQueueReference(String key) { 161 return queues.get(key); 162 } 163 164 @Override 165 @SuppressWarnings("unchecked") 166 protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception { 167 int consumers = getAndRemoveParameter(parameters, "concurrentConsumers", Integer.class, concurrentConsumers); 168 boolean limitConcurrentConsumers = getAndRemoveParameter(parameters, "limitConcurrentConsumers", Boolean.class, true); 169 if (limitConcurrentConsumers && consumers > maxConcurrentConsumers) { 170 throw new IllegalArgumentException("The limitConcurrentConsumers flag in set to true. ConcurrentConsumers cannot be set at a value greater than " 171 + maxConcurrentConsumers + " was " + consumers); 172 } 173 // Resolve queue reference 174 BlockingQueue<Exchange> queue = resolveAndRemoveReferenceParameter(parameters, "queue", BlockingQueue.class); 175 SedaEndpoint answer; 176 // Resolve queue factory when no queue specified 177 if (queue == null) { 178 BlockingQueueFactory<Exchange> queueFactory = resolveAndRemoveReferenceParameter(parameters, "queueFactory", BlockingQueueFactory.class); 179 // defer creating queue till endpoint is started, so we pass the queue factory 180 answer = createEndpoint(uri, this, queueFactory, consumers); 181 } else { 182 answer = createEndpoint(uri, this, queue, consumers); 183 } 184 answer.configureProperties(parameters); 185 return answer; 186 } 187 188 protected SedaEndpoint createEndpoint(String endpointUri, Component component, BlockingQueueFactory<Exchange> queueFactory, int concurrentConsumers) { 189 return new SedaEndpoint(endpointUri, component, queueFactory, concurrentConsumers); 190 } 191 192 protected SedaEndpoint createEndpoint(String endpointUri, Component component, BlockingQueue<Exchange> queue, int concurrentConsumers) { 193 return new SedaEndpoint(endpointUri, component, queue, concurrentConsumers); 194 } 195 196 public String getQueueKey(String uri) { 197 if (uri.contains("?")) { 198 // strip parameters 199 uri = uri.substring(0, uri.indexOf('?')); 200 } 201 return uri; 202 } 203 204 @Override 205 protected void doStop() throws Exception { 206 getQueues().clear(); 207 super.doStop(); 208 } 209 210 /** 211 * On shutting down the endpoint 212 * 213 * @param endpoint the endpoint 214 */ 215 void onShutdownEndpoint(SedaEndpoint endpoint) { 216 // we need to remove the endpoint from the reference counter 217 String key = getQueueKey(endpoint.getEndpointUri()); 218 QueueReference ref = getQueues().get(key); 219 if (ref != null && endpoint.getConsumers().size() == 0) { 220 // only remove the endpoint when the consumers are removed 221 ref.removeReference(endpoint); 222 if (ref.getCount() <= 0) { 223 // reference no longer needed so remove from queues 224 getQueues().remove(key); 225 } 226 } 227 } 228 229}