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