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