/* * Copyright 2014-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.amqp.rabbit.listener; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.springframework.amqp.core.MessageListener; import org.springframework.amqp.core.Queue; import org.springframework.amqp.rabbit.config.SimpleRabbitListenerEndpoint; import org.springframework.amqp.rabbit.core.RabbitAdmin; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.config.BeanExpressionContext; import org.springframework.beans.factory.config.BeanExpressionResolver; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.expression.BeanFactoryResolver; import org.springframework.expression.BeanResolver; import org.springframework.util.Assert; /** * Base model for a Rabbit listener endpoint. * * @author Stephane Nicoll * @author Gary Russell * @since 1.4 * @see MethodRabbitListenerEndpoint * @see SimpleRabbitListenerEndpoint */ public abstract class AbstractRabbitListenerEndpoint implements RabbitListenerEndpoint, BeanFactoryAware { private String id; private final Collection<Queue> queues = new ArrayList<Queue>(); private final Collection<String> queueNames = new ArrayList<String>(); private boolean exclusive; private Integer priority; private String concurrency; private RabbitAdmin admin; private BeanFactory beanFactory; private BeanExpressionResolver resolver; private BeanExpressionContext expressionContext; private BeanResolver beanResolver; private String group; @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; if (beanFactory instanceof ConfigurableListableBeanFactory) { this.resolver = ((ConfigurableListableBeanFactory) beanFactory).getBeanExpressionResolver(); this.expressionContext = new BeanExpressionContext((ConfigurableListableBeanFactory) beanFactory, null); } this.beanResolver = new BeanFactoryResolver(beanFactory); } protected BeanFactory getBeanFactory() { return this.beanFactory; } protected BeanExpressionResolver getResolver() { return this.resolver; } protected BeanResolver getBeanResolver() { return this.beanResolver; } protected BeanExpressionContext getBeanExpressionContext() { return this.expressionContext; } public void setId(String id) { this.id = id; } @Override public String getId() { return this.id; } /** * Set the queues to use. Either the {@link Queue} instances or the * queue names should be provided, but not both. * @param queues to set. * @see #setQueueNames */ public void setQueues(Queue... queues) { Assert.notNull(queues, "'queues' must not be null"); this.queues.clear(); this.queues.addAll(Arrays.asList(queues)); } /** * @return the queues for this endpoint. */ public Collection<Queue> getQueues() { return this.queues; } /** * @return the queue names for this endpoint. */ public Collection<String> getQueueNames() { return this.queueNames; } /** * Set the queue names to use. Either the {@link Queue} instances or the * queue names should be provided, but not both. * @param queueNames to set. * @see #setQueues */ public void setQueueNames(String... queueNames) { Assert.notNull(queueNames, "'queueNames' must not be null"); this.queueNames.clear(); this.queueNames.addAll(Arrays.asList(queueNames)); } /** * Set if a single consumer in the container will have exclusive use of the * queues, preventing other consumers from receiving messages from the * queue(s). * @param exclusive the exclusive {@code boolean} flag. */ public void setExclusive(boolean exclusive) { this.exclusive = exclusive; } /** * @return the exclusive {@code boolean} flag. */ public boolean isExclusive() { return this.exclusive; } /** * Set the priority of this endpoint. * @param priority the priority value. */ public void setPriority(Integer priority) { this.priority = priority; } /** * @return the priority of this endpoint or {@code null} if * no priority is set. */ public Integer getPriority() { return this.priority; } /** * Set the concurrency of this endpoint; usually overrides any concurrency * settings on the container factory. Contents depend on container implementation. * @param concurrency the concurrency. * @since 2.0 */ public void setConcurrency(String concurrency) { this.concurrency = concurrency; } /** * The concurrency of this endpoint; Not used by this abstract class; * used by subclasses to set the concurrency appropriate for the container type. * @return the concurrency. * @since 2.0 */ @Override public String getConcurrency() { return this.concurrency; } /** * Set the {@link RabbitAdmin} instance to use. * @param admin the {@link RabbitAdmin} instance. */ public void setAdmin(RabbitAdmin admin) { this.admin = admin; } /** * @return the {@link RabbitAdmin} instance to use or {@code null} if * none is configured. */ public RabbitAdmin getAdmin() { return this.admin; } @Override public String getGroup() { return this.group; } /** * Set the group for the corresponding listener container. * @param group the group. * @since 1.5 */ public void setGroup(String group) { this.group = group; } @Override public void setupListenerContainer(MessageListenerContainer listenerContainer) { AbstractMessageListenerContainer container = (AbstractMessageListenerContainer) listenerContainer; boolean queuesEmpty = getQueues().isEmpty(); boolean queueNamesEmpty = getQueueNames().isEmpty(); if (!queuesEmpty && !queueNamesEmpty) { throw new IllegalStateException("Queues or queue names must be provided but not both for " + this); } if (queuesEmpty) { Collection<String> names = getQueueNames(); container.setQueueNames(names.toArray(new String[names.size()])); } else { Collection<Queue> instances = getQueues(); container.setQueues(instances.toArray(new Queue[instances.size()])); } container.setExclusive(isExclusive()); if (getPriority() != null) { Map<String, Object> args = new HashMap<String, Object>(); args.put("x-priority", getPriority()); container.setConsumerArguments(args); } if (getAdmin() != null) { container.setRabbitAdmin(getAdmin()); } setupMessageListener(listenerContainer); } /** * Create a {@link MessageListener} that is able to serve this endpoint for the * specified container. * @param container the {@link MessageListenerContainer} to create a {@link MessageListener}. * @return a a {@link MessageListener} instance. */ protected abstract MessageListener createMessageListener(MessageListenerContainer container); private void setupMessageListener(MessageListenerContainer container) { MessageListener messageListener = createMessageListener(container); Assert.state(messageListener != null, () -> "Endpoint [" + this + "] must provide a non null message listener"); container.setupMessageListener(messageListener); } /** * @return a description for this endpoint. * <p>Available to subclasses, for inclusion in their {@code toString()} result. */ protected StringBuilder getEndpointDescription() { StringBuilder result = new StringBuilder(); return result.append(getClass().getSimpleName()).append("[").append(this.id). append("] queues=").append(this.queues). append("' | queueNames='").append(this.queueNames). append("' | exclusive='").append(this.exclusive). append("' | priority='").append(this.priority). append("' | admin='").append(this.admin).append("'"); } @Override public String toString() { return getEndpointDescription().toString(); } }