diff options
Diffstat (limited to 'spring-jms/src/main/java/org/springframework')
21 files changed, 494 insertions, 105 deletions
diff --git a/spring-jms/src/main/java/org/springframework/jms/UncategorizedJmsException.java b/spring-jms/src/main/java/org/springframework/jms/UncategorizedJmsException.java index b9531906..e55ba0d6 100644 --- a/spring-jms/src/main/java/org/springframework/jms/UncategorizedJmsException.java +++ b/spring-jms/src/main/java/org/springframework/jms/UncategorizedJmsException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -51,7 +51,7 @@ public class UncategorizedJmsException extends JmsException { * but can also be a JNDI NamingException or the like. */ public UncategorizedJmsException(Throwable cause) { - super("Uncategorized exception occured during JMS processing", cause); + super("Uncategorized exception occurred during JMS processing", cause); } } diff --git a/spring-jms/src/main/java/org/springframework/jms/annotation/EnableJms.java b/spring-jms/src/main/java/org/springframework/jms/annotation/EnableJms.java index a8767010..0d908450 100644 --- a/spring-jms/src/main/java/org/springframework/jms/annotation/EnableJms.java +++ b/spring-jms/src/main/java/org/springframework/jms/annotation/EnableJms.java @@ -108,7 +108,7 @@ import org.springframework.context.annotation.Import; * <p>Annotated methods can use flexible signature; in particular, it is possible to use * the {@link org.springframework.messaging.Message Message} abstraction and related annotations, * see {@link JmsListener} Javadoc for more details. For instance, the following would - * inject the content of the message and a a custom "myCounter" JMS header: + * inject the content of the message and a custom "myCounter" JMS header: * * <pre class="code"> * @JmsListener(containerFactory = "myJmsListenerContainerFactory", destination="myQueue") diff --git a/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListener.java b/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListener.java index bf62c493..78329881 100644 --- a/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListener.java +++ b/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -66,6 +66,9 @@ import org.springframework.messaging.handler.annotation.MessageMapping; * {@link org.springframework.messaging.handler.annotation.SendTo @SendTo} to the * method declaration. * + * <p>This annotation may be used as a <em>meta-annotation</em> to create custom + * <em>composed annotations</em> with attribute overrides. + * * @author Stephane Nicoll * @since 4.1 * @see EnableJms @@ -111,7 +114,8 @@ public @interface JmsListener { String selector() default ""; /** - * The concurrency limits for the listener, if any. + * The concurrency limits for the listener, if any. Overrides the value defined + * by the container factory used to create the listener container. * <p>The concurrency limits can be a "lower-upper" String — for example, * "5-10" — or a simple upper limit String — for example, "10", in * which case the lower limit will be 1. diff --git a/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListenerAnnotationBeanPostProcessor.java b/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListenerAnnotationBeanPostProcessor.java index 10b35b7e..dad78130 100644 --- a/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListenerAnnotationBeanPostProcessor.java +++ b/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListenerAnnotationBeanPostProcessor.java @@ -36,9 +36,10 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.beans.factory.config.EmbeddedValueResolver; import org.springframework.core.MethodIntrospector; import org.springframework.core.Ordered; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.jms.config.JmsListenerConfigUtils; import org.springframework.jms.config.JmsListenerContainerFactory; import org.springframework.jms.config.JmsListenerEndpointRegistrar; @@ -49,6 +50,7 @@ import org.springframework.messaging.handler.annotation.support.MessageHandlerMe import org.springframework.messaging.handler.invocation.InvocableHandlerMethod; import org.springframework.util.Assert; import org.springframework.util.StringUtils; +import org.springframework.util.StringValueResolver; /** * Bean post-processor that registers methods annotated with {@link JmsListener} @@ -95,6 +97,8 @@ public class JmsListenerAnnotationBeanPostProcessor private BeanFactory beanFactory; + private StringValueResolver embeddedValueResolver; + private final MessageHandlerMethodFactoryAdapter messageHandlerMethodFactory = new MessageHandlerMethodFactoryAdapter(); private final JmsListenerEndpointRegistrar registrar = new JmsListenerEndpointRegistrar(); @@ -146,6 +150,9 @@ public class JmsListenerAnnotationBeanPostProcessor @Override public void setBeanFactory(BeanFactory beanFactory) { this.beanFactory = beanFactory; + if (beanFactory instanceof ConfigurableBeanFactory) { + this.embeddedValueResolver = new EmbeddedValueResolver((ConfigurableBeanFactory) beanFactory); + } } @@ -198,7 +205,7 @@ public class JmsListenerAnnotationBeanPostProcessor new MethodIntrospector.MetadataLookup<Set<JmsListener>>() { @Override public Set<JmsListener> inspect(Method method) { - Set<JmsListener> listenerMethods = AnnotationUtils.getRepeatableAnnotations( + Set<JmsListener> listenerMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations( method, JmsListener.class, JmsListeners.class); return (!listenerMethods.isEmpty() ? listenerMethods : null); } @@ -236,13 +243,14 @@ public class JmsListenerAnnotationBeanPostProcessor * @see JmsListenerEndpointRegistrar#registerEndpoint */ protected void processJmsListener(JmsListener jmsListener, Method mostSpecificMethod, Object bean) { - Method invocableMethod = MethodIntrospector.selectInvocableMethod(mostSpecificMethod, bean.getClass()); + Method invocableMethod = AopUtils.selectInvocableMethod(mostSpecificMethod, bean.getClass()); MethodJmsListenerEndpoint endpoint = createMethodJmsListenerEndpoint(); endpoint.setBean(bean); endpoint.setMethod(invocableMethod); endpoint.setMostSpecificMethod(mostSpecificMethod); endpoint.setMessageHandlerMethodFactory(this.messageHandlerMethodFactory); + endpoint.setEmbeddedValueResolver(this.embeddedValueResolver); endpoint.setBeanFactory(this.beanFactory); endpoint.setId(getEndpointId(jmsListener)); endpoint.setDestination(resolve(jmsListener.destination())); @@ -294,8 +302,7 @@ public class JmsListenerAnnotationBeanPostProcessor } private String resolve(String value) { - return (this.beanFactory instanceof ConfigurableBeanFactory ? - ((ConfigurableBeanFactory) this.beanFactory).resolveEmbeddedValue(value) : value); + return (this.embeddedValueResolver != null ? this.embeddedValueResolver.resolveStringValue(value) : value); } diff --git a/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListeners.java b/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListeners.java index f3d5266e..255f6457 100644 --- a/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListeners.java +++ b/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListeners.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -30,6 +30,9 @@ import java.lang.annotation.Target; * where {@link JmsListener} can simply be declared several times on the same method, * implicitly generating this container annotation. * + * <p>This annotation may be used as a <em>meta-annotation</em> to create custom + * <em>composed annotations</em>. + * * @author Stephane Nicoll * @since 4.2 * @see JmsListener diff --git a/spring-jms/src/main/java/org/springframework/jms/config/JcaListenerContainerParser.java b/spring-jms/src/main/java/org/springframework/jms/config/JcaListenerContainerParser.java index ef03eda9..9f005d06 100644 --- a/spring-jms/src/main/java/org/springframework/jms/config/JcaListenerContainerParser.java +++ b/spring-jms/src/main/java/org/springframework/jms/config/JcaListenerContainerParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -88,7 +88,7 @@ class JcaListenerContainerParser extends AbstractListenerContainerParser { String prefetch = containerEle.getAttribute(PREFETCH_ATTRIBUTE); if (StringUtils.hasText(prefetch)) { - properties.add("prefetchSize", new Integer(prefetch)); + properties.add("prefetchSize", Integer.valueOf(prefetch)); } return properties; diff --git a/spring-jms/src/main/java/org/springframework/jms/config/MethodJmsListenerEndpoint.java b/spring-jms/src/main/java/org/springframework/jms/config/MethodJmsListenerEndpoint.java index 8bde24d0..b7379d20 100644 --- a/spring-jms/src/main/java/org/springframework/jms/config/MethodJmsListenerEndpoint.java +++ b/spring-jms/src/main/java/org/springframework/jms/config/MethodJmsListenerEndpoint.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -22,8 +22,10 @@ import java.util.Arrays; import org.springframework.aop.framework.AopProxyUtils; import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.beans.factory.config.EmbeddedValueResolver; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.jms.listener.MessageListenerContainer; import org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter; import org.springframework.jms.support.converter.MessageConverter; @@ -33,6 +35,7 @@ import org.springframework.messaging.handler.annotation.support.MessageHandlerMe import org.springframework.messaging.handler.invocation.InvocableHandlerMethod; import org.springframework.util.Assert; import org.springframework.util.StringUtils; +import org.springframework.util.StringValueResolver; /** * A {@link JmsListenerEndpoint} providing the method to invoke to process @@ -42,7 +45,7 @@ import org.springframework.util.StringUtils; * @author Juergen Hoeller * @since 4.1 */ -public class MethodJmsListenerEndpoint extends AbstractJmsListenerEndpoint { +public class MethodJmsListenerEndpoint extends AbstractJmsListenerEndpoint implements BeanFactoryAware { private Object bean; @@ -52,6 +55,8 @@ public class MethodJmsListenerEndpoint extends AbstractJmsListenerEndpoint { private MessageHandlerMethodFactory messageHandlerMethodFactory; + private StringValueResolver embeddedValueResolver; + private BeanFactory beanFactory; @@ -110,12 +115,24 @@ public class MethodJmsListenerEndpoint extends AbstractJmsListenerEndpoint { } /** - * Set the {@link BeanFactory} to use to resolve expressions (can be null). + * Set a value resolver for embedded placeholders and expressions. */ + public void setEmbeddedValueResolver(StringValueResolver embeddedValueResolver) { + this.embeddedValueResolver = embeddedValueResolver; + } + + /** + * Set the {@link BeanFactory} to use to resolve expressions (can be {@code null}). + */ + @Override public void setBeanFactory(BeanFactory beanFactory) { this.beanFactory = beanFactory; + if (this.embeddedValueResolver == null && beanFactory instanceof ConfigurableBeanFactory) { + this.embeddedValueResolver = new EmbeddedValueResolver((ConfigurableBeanFactory) beanFactory); + } } + @Override protected MessagingMessageListenerAdapter createMessageListener(MessageListenerContainer container) { Assert.state(this.messageHandlerMethodFactory != null, @@ -157,7 +174,7 @@ public class MethodJmsListenerEndpoint extends AbstractJmsListenerEndpoint { */ protected String getDefaultResponseDestination() { Method specificMethod = getMostSpecificMethod(); - SendTo ann = AnnotationUtils.getAnnotation(specificMethod, SendTo.class); + SendTo ann = getSendTo(specificMethod); if (ann != null) { Object[] destinations = ann.value(); if (destinations.length != 1) { @@ -169,15 +186,16 @@ public class MethodJmsListenerEndpoint extends AbstractJmsListenerEndpoint { return null; } - /** - * Resolve the specified value if possible. - * @see ConfigurableBeanFactory#resolveEmbeddedValue - */ - private String resolve(String value) { - if (this.beanFactory instanceof ConfigurableBeanFactory) { - return ((ConfigurableBeanFactory) this.beanFactory).resolveEmbeddedValue(value); + private SendTo getSendTo(Method specificMethod) { + SendTo ann = AnnotatedElementUtils.findMergedAnnotation(specificMethod, SendTo.class); + if (ann == null) { + ann = AnnotatedElementUtils.findMergedAnnotation(specificMethod.getDeclaringClass(), SendTo.class); } - return value; + return ann; + } + + private String resolve(String value) { + return (this.embeddedValueResolver != null ? this.embeddedValueResolver.resolveStringValue(value) : value); } diff --git a/spring-jms/src/main/java/org/springframework/jms/connection/CachingConnectionFactory.java b/spring-jms/src/main/java/org/springframework/jms/connection/CachingConnectionFactory.java index f3ec7bee..14eb99ff 100644 --- a/spring-jms/src/main/java/org/springframework/jms/connection/CachingConnectionFactory.java +++ b/spring-jms/src/main/java/org/springframework/jms/connection/CachingConnectionFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -509,7 +509,7 @@ public class CachingConnectionFactory extends SingleConnectionFactory { * Simple wrapper class around a Destination reference. * Used as the cache key when caching MessageProducer objects. */ - private static class DestinationCacheKey { + private static class DestinationCacheKey implements Comparable<DestinationCacheKey> { private final Destination destination; @@ -537,7 +537,7 @@ public class CachingConnectionFactory extends SingleConnectionFactory { public boolean equals(Object other) { // Effectively checking object equality as well as toString equality. // On WebSphere MQ, Destination objects do not implement equals... - return (other == this || destinationEquals((DestinationCacheKey) other)); + return (this == other || destinationEquals((DestinationCacheKey) other)); } @Override @@ -547,6 +547,16 @@ public class CachingConnectionFactory extends SingleConnectionFactory { // for equivalent destinations... Thanks a lot, WebSphere MQ! return this.destination.getClass().hashCode(); } + + @Override + public String toString() { + return getDestinationString(); + } + + @Override + public int compareTo(DestinationCacheKey other) { + return getDestinationString().compareTo(other.getDestinationString()); + } } @@ -584,6 +594,12 @@ public class CachingConnectionFactory extends SingleConnectionFactory { ObjectUtils.nullSafeEquals(this.subscription, otherKey.subscription) && this.durable == otherKey.durable); } + + @Override + public String toString() { + return super.toString() + " [selector=" + this.selector + ", noLocal=" + this.noLocal + + ", subscription=" + this.subscription + ", durable=" + this.durable + "]"; + } } } diff --git a/spring-jms/src/main/java/org/springframework/jms/connection/SessionProxy.java b/spring-jms/src/main/java/org/springframework/jms/connection/SessionProxy.java index bf439aee..e866b9ff 100644 --- a/spring-jms/src/main/java/org/springframework/jms/connection/SessionProxy.java +++ b/spring-jms/src/main/java/org/springframework/jms/connection/SessionProxy.java @@ -20,7 +20,7 @@ import javax.jms.Session; /** * Subinterface of {@link javax.jms.Session} to be implemented by - * Session proxies. Allows access to the the underlying target Session. + * Session proxies. Allows access to the underlying target Session. * * @author Juergen Hoeller * @since 2.0.4 diff --git a/spring-jms/src/main/java/org/springframework/jms/connection/TransactionAwareConnectionFactoryProxy.java b/spring-jms/src/main/java/org/springframework/jms/connection/TransactionAwareConnectionFactoryProxy.java index 50c61409..19a9ffc0 100644 --- a/spring-jms/src/main/java/org/springframework/jms/connection/TransactionAwareConnectionFactoryProxy.java +++ b/spring-jms/src/main/java/org/springframework/jms/connection/TransactionAwareConnectionFactoryProxy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -101,7 +101,7 @@ public class TransactionAwareConnectionFactoryProxy * Set the target ConnectionFactory that this ConnectionFactory should delegate to. */ public final void setTargetConnectionFactory(ConnectionFactory targetConnectionFactory) { - Assert.notNull(targetConnectionFactory, "targetConnectionFactory must not be nul"); + Assert.notNull(targetConnectionFactory, "'targetConnectionFactory' must not be null"); this.targetConnectionFactory = targetConnectionFactory; } diff --git a/spring-jms/src/main/java/org/springframework/jms/core/JmsTemplate.java b/spring-jms/src/main/java/org/springframework/jms/core/JmsTemplate.java index 4407c000..d20fb208 100644 --- a/spring-jms/src/main/java/org/springframework/jms/core/JmsTemplate.java +++ b/spring-jms/src/main/java/org/springframework/jms/core/JmsTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -89,18 +89,6 @@ import org.springframework.util.ReflectionUtils; */ public class JmsTemplate extends JmsDestinationAccessor implements JmsOperations { - /** - * Timeout value indicating that a receive operation should - * check if a message is immediately available without blocking. - */ - public static final long RECEIVE_TIMEOUT_NO_WAIT = -1; - - /** - * Timeout value indicating a blocking receive without timeout. - */ - public static final long RECEIVE_TIMEOUT_INDEFINITE_WAIT = 0; - - /** The JMS 2.0 MessageProducer.setDeliveryDelay method, if available */ private static final Method setDeliveryDelayMethod = ClassUtils.getMethodIfAvailable(MessageProducer.class, "setDeliveryDelay", long.class); @@ -315,11 +303,13 @@ public class JmsTemplate extends JmsDestinationAccessor implements JmsOperations * Set the timeout to use for receive calls (in milliseconds). * <p>The default is {@link #RECEIVE_TIMEOUT_INDEFINITE_WAIT}, which indicates * a blocking receive without timeout. - * <p>Specify {@link #RECEIVE_TIMEOUT_NO_WAIT} to inidicate that a receive operation - * should check if a message is immediately available without blocking. + * <p>Specify {@link #RECEIVE_TIMEOUT_NO_WAIT} (or any other negative value) + * to indicate that a receive operation should check if a message is + * immediately available without blocking. + * @see #receiveFromConsumer(MessageConsumer, long) * @see javax.jms.MessageConsumer#receive(long) - * @see javax.jms.MessageConsumer#receive() * @see javax.jms.MessageConsumer#receiveNoWait() + * @see javax.jms.MessageConsumer#receive() */ public void setReceiveTimeout(long receiveTimeout) { this.receiveTimeout = receiveTimeout; @@ -800,7 +790,7 @@ public class JmsTemplate extends JmsDestinationAccessor implements JmsOperations if (resourceHolder != null && resourceHolder.hasTimeout()) { timeout = Math.min(timeout, resourceHolder.getTimeToLiveInMillis()); } - Message message = doReceive(consumer, timeout); + Message message = receiveFromConsumer(consumer, timeout); if (session.getTransacted()) { // Commit necessary - but avoid commit call within a JTA transaction. if (isSessionLocallyTransacted(session)) { @@ -821,25 +811,6 @@ public class JmsTemplate extends JmsDestinationAccessor implements JmsOperations } } - /** - * Actually receive a message from the given consumer. - * @param consumer the JMS MessageConsumer to receive with - * @param timeout the receive timeout - * @return the JMS Message received, or {@code null} if none - * @throws JMSException if thrown by JMS API methods - */ - private Message doReceive(MessageConsumer consumer, long timeout) throws JMSException { - if (timeout == RECEIVE_TIMEOUT_NO_WAIT) { - return consumer.receiveNoWait(); - } - else if (timeout > 0) { - return consumer.receive(timeout); - } - else { - return consumer.receive(); - } - } - //--------------------------------------------------------------------------------------- // Convenience methods for receiving auto-converted messages @@ -952,7 +923,7 @@ public class JmsTemplate extends JmsDestinationAccessor implements JmsOperations logger.debug("Sending created message: " + requestMessage); } doSend(producer, requestMessage); - return doReceive(consumer, getReceiveTimeout()); + return receiveFromConsumer(consumer, getReceiveTimeout()); } finally { JmsUtils.closeMessageConsumer(consumer); diff --git a/spring-jms/src/main/java/org/springframework/jms/listener/AbstractPollingMessageListenerContainer.java b/spring-jms/src/main/java/org/springframework/jms/listener/AbstractPollingMessageListenerContainer.java index 4c1a6011..555cd6b8 100644 --- a/spring-jms/src/main/java/org/springframework/jms/listener/AbstractPollingMessageListenerContainer.java +++ b/spring-jms/src/main/java/org/springframework/jms/listener/AbstractPollingMessageListenerContainer.java @@ -158,9 +158,13 @@ public abstract class AbstractPollingMessageListenerContainer extends AbstractMe * The default is 1000 ms, that is, 1 second. * <p><b>NOTE:</b> This value needs to be smaller than the transaction * timeout used by the transaction manager (in the appropriate unit, - * of course). -1 indicates no timeout at all; however, this is only - * feasible if not running within a transaction manager. + * of course). 0 indicates no timeout at all; however, this is only + * feasible if not running within a transaction manager and generally + * discouraged since such a listener container cannot cleanly shut down. + * A negative value such as -1 indicates a no-wait receive operation. + * @see #receiveFromConsumer(MessageConsumer, long) * @see javax.jms.MessageConsumer#receive(long) + * @see javax.jms.MessageConsumer#receiveNoWait() * @see javax.jms.MessageConsumer#receive() * @see #setTransactionTimeout */ @@ -417,7 +421,7 @@ public abstract class AbstractPollingMessageListenerContainer extends AbstractMe * @throws JMSException if thrown by JMS methods */ protected Message receiveMessage(MessageConsumer consumer) throws JMSException { - return (this.receiveTimeout < 0 ? consumer.receive() : consumer.receive(this.receiveTimeout)); + return receiveFromConsumer(consumer, getReceiveTimeout()); } /** diff --git a/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java b/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java index 5fe49eb4..f9f6479f 100644 --- a/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java +++ b/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java @@ -995,7 +995,9 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe } else { try { - Thread.sleep(interval); + synchronized (this.lifecycleMonitor) { + this.lifecycleMonitor.wait(interval); + } } catch (InterruptedException interEx) { // Re-interrupt current thread, to allow other threads to react. @@ -1063,9 +1065,9 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe catch (Throwable ex) { clearResources(); if (!this.lastMessageSucceeded) { - // We failed more than once in a row or on startup - sleep before - // first recovery attempt. - sleepBeforeRecoveryAttempt(); + // We failed more than once in a row or on startup - + // wait before first recovery attempt. + waitBeforeRecoveryAttempt(); } this.lastMessageSucceeded = false; boolean alreadyRecovered = false; @@ -1220,11 +1222,11 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe /** * Apply the back-off time once. In a regular scenario, the back-off is only applied if we - * failed to recover with the broker. This additional sleep period avoids a burst retry + * failed to recover with the broker. This additional wait period avoids a burst retry * scenario when the broker is actually up but something else if failing (i.e. listener * specific). */ - private void sleepBeforeRecoveryAttempt() { + private void waitBeforeRecoveryAttempt() { BackOffExecution execution = DefaultMessageListenerContainer.this.backOff.start(); applyBackOffTime(execution); } diff --git a/spring-jms/src/main/java/org/springframework/jms/listener/adapter/AbstractAdaptableMessageListener.java b/spring-jms/src/main/java/org/springframework/jms/listener/adapter/AbstractAdaptableMessageListener.java index 10ae1cf8..2f53f57e 100644 --- a/spring-jms/src/main/java/org/springframework/jms/listener/adapter/AbstractAdaptableMessageListener.java +++ b/spring-jms/src/main/java/org/springframework/jms/listener/adapter/AbstractAdaptableMessageListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -36,8 +36,10 @@ import org.springframework.jms.support.converter.MessageConversionException; import org.springframework.jms.support.converter.MessageConverter; import org.springframework.jms.support.converter.MessagingMessageConverter; import org.springframework.jms.support.converter.SimpleMessageConverter; +import org.springframework.jms.support.converter.SmartMessageConverter; import org.springframework.jms.support.destination.DestinationResolver; import org.springframework.jms.support.destination.DynamicDestinationResolver; +import org.springframework.messaging.MessageHeaders; import org.springframework.util.Assert; /** @@ -269,14 +271,17 @@ public abstract class AbstractAdaptableMessageListener * @see #setMessageConverter */ protected Message buildMessage(Session session, Object result) throws JMSException { - Object content = (result instanceof JmsResponse ? ((JmsResponse<?>) result).getResponse() : result); - if (content instanceof org.springframework.messaging.Message) { - return this.messagingMessageConverter.toMessage(content, session); - } + Object content = preProcessResponse(result instanceof JmsResponse + ? ((JmsResponse<?>) result).getResponse() : result); MessageConverter converter = getMessageConverter(); if (converter != null) { - return converter.toMessage(content, session); + if (content instanceof org.springframework.messaging.Message) { + return this.messagingMessageConverter.toMessage(content, session); + } + else { + return converter.toMessage(content, session); + } } if (!(content instanceof Message)) { @@ -287,6 +292,17 @@ public abstract class AbstractAdaptableMessageListener } /** + * Pre-process the given result before it is converted to a {@link Message}. + * @param result the result of the invocation + * @return the payload response to handle, either the {@code result} argument + * or any other object (for instance wrapping the result). + * @since 4.3 + */ + protected Object preProcessResponse(Object result) { + return result; + } + + /** * Post-process the given response message before it will be sent. * <p>The default implementation sets the response's correlation id * to the request message's correlation id, if any; otherwise to the @@ -402,11 +418,21 @@ public abstract class AbstractAdaptableMessageListener /** - * Delegates payload extraction to {@link #extractMessage(javax.jms.Message)} to - * enforce backward compatibility. + * A {@link MessagingMessageConverter} that lazily invoke payload extraction and + * delegate it to {@link #extractMessage(javax.jms.Message)} in order to enforce + * backward compatibility. */ private class MessagingMessageConverterAdapter extends MessagingMessageConverter { + @SuppressWarnings("unchecked") + @Override + public Object fromMessage(javax.jms.Message message) throws JMSException, MessageConversionException { + if (message == null) { + return null; + } + return new LazyResolutionMessage(message); + } + @Override protected Object extractPayload(Message message) throws JMSException { Object payload = extractMessage(message); @@ -425,13 +451,69 @@ public abstract class AbstractAdaptableMessageListener } @Override - protected Message createMessageForPayload(Object payload, Session session) throws JMSException { + protected Message createMessageForPayload(Object payload, Session session, Object conversionHint) + throws JMSException { MessageConverter converter = getMessageConverter(); - if (converter != null) { - return converter.toMessage(payload, session); + if (converter == null) { + throw new IllegalStateException("No message converter, cannot handle '" + payload + "'"); } - throw new IllegalStateException("No message converter - cannot handle [" + payload + "]"); + if (converter instanceof SmartMessageConverter) { + return ((SmartMessageConverter) converter).toMessage(payload, session, conversionHint); + + } + return converter.toMessage(payload, session); } + + protected class LazyResolutionMessage implements org.springframework.messaging.Message<Object> { + + private final javax.jms.Message message; + + private Object payload; + + private MessageHeaders headers; + + public LazyResolutionMessage(javax.jms.Message message) { + this.message = message; + } + + @Override + public Object getPayload() { + if (this.payload == null) { + try { + this.payload = unwrapPayload(); + } + catch (JMSException ex) { + throw new MessageConversionException( + "Failed to extract payload from [" + this.message + "]", ex); + } + } + // + return this.payload; + } + + /** + * Extract the payload of the current message. Since we deferred the resolution + * of the payload, a custom converter may still return a full message for it. In + * this case, its payload is returned. + * @return the payload of the message + */ + private Object unwrapPayload() throws JMSException { + Object payload = extractPayload(this.message); + if (payload instanceof org.springframework.messaging.Message) { + return ((org.springframework.messaging.Message) payload).getPayload(); + } + return payload; + } + + @Override + public MessageHeaders getHeaders() { + if (this.headers == null) { + this.headers = extractHeaders(this.message); + } + return this.headers; + } + } + } diff --git a/spring-jms/src/main/java/org/springframework/jms/listener/adapter/MessagingMessageListenerAdapter.java b/spring-jms/src/main/java/org/springframework/jms/listener/adapter/MessagingMessageListenerAdapter.java index 0791f4c4..b87dd6b2 100644 --- a/spring-jms/src/main/java/org/springframework/jms/listener/adapter/MessagingMessageListenerAdapter.java +++ b/spring-jms/src/main/java/org/springframework/jms/listener/adapter/MessagingMessageListenerAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -19,11 +19,14 @@ package org.springframework.jms.listener.adapter; import javax.jms.JMSException; import javax.jms.Session; +import org.springframework.core.MethodParameter; import org.springframework.jms.support.JmsHeaderMapper; import org.springframework.jms.support.converter.MessageConversionException; import org.springframework.messaging.Message; import org.springframework.messaging.MessagingException; +import org.springframework.messaging.core.AbstractMessageSendingTemplate; import org.springframework.messaging.handler.invocation.InvocableHandlerMethod; +import org.springframework.messaging.support.MessageBuilder; /** * A {@link javax.jms.MessageListener} adapter that invokes a configurable @@ -72,6 +75,17 @@ public class MessagingMessageListenerAdapter extends AbstractAdaptableMessageLis } } + @Override + protected Object preProcessResponse(Object result) { + MethodParameter returnType = this.handlerMethod.getReturnType(); + if (result instanceof Message) { + return MessageBuilder.fromMessage((Message<?>) result) + .setHeader(AbstractMessageSendingTemplate.CONVERSION_HINT_HEADER, returnType).build(); + } + return MessageBuilder.withPayload(result).setHeader( + AbstractMessageSendingTemplate.CONVERSION_HINT_HEADER, returnType).build(); + } + protected Message<?> toMessagingMessage(javax.jms.Message jmsMessage) { try { return (Message<?>) getMessagingMessageConverter().fromMessage(jmsMessage); diff --git a/spring-jms/src/main/java/org/springframework/jms/listener/endpoint/JmsMessageEndpointFactory.java b/spring-jms/src/main/java/org/springframework/jms/listener/endpoint/JmsMessageEndpointFactory.java index fe448aa2..5beb8733 100644 --- a/spring-jms/src/main/java/org/springframework/jms/listener/endpoint/JmsMessageEndpointFactory.java +++ b/spring-jms/src/main/java/org/springframework/jms/listener/endpoint/JmsMessageEndpointFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -61,7 +61,7 @@ public class JmsMessageEndpointFactory extends AbstractMessageEndpointFactory { * Return the JMS MessageListener for this endpoint. */ protected MessageListener getMessageListener() { - return messageListener; + return this.messageListener; } /** diff --git a/spring-jms/src/main/java/org/springframework/jms/support/JmsAccessor.java b/spring-jms/src/main/java/org/springframework/jms/support/JmsAccessor.java index 38538c73..05352cf1 100644 --- a/spring-jms/src/main/java/org/springframework/jms/support/JmsAccessor.java +++ b/spring-jms/src/main/java/org/springframework/jms/support/JmsAccessor.java @@ -125,7 +125,7 @@ public abstract class JmsAccessor implements InitializingBean { * {@link Session} to send a message. * <p>Default is {@link Session#AUTO_ACKNOWLEDGE}. * <p>Vendor-specific extensions to the acknowledgment mode can be set here as well. - * <p>Note that that inside an EJB, the parameters to the + * <p>Note that inside an EJB, the parameters to the * {@code create(Queue/Topic)Session(boolean transacted, int acknowledgeMode)} method * are not taken into account. Depending on the transaction context in the EJB, * the container makes its own decisions on these values. See section 17.3.5 diff --git a/spring-jms/src/main/java/org/springframework/jms/support/converter/MappingJackson2MessageConverter.java b/spring-jms/src/main/java/org/springframework/jms/support/converter/MappingJackson2MessageConverter.java index b64abf2f..5d9bc44b 100644 --- a/spring-jms/src/main/java/org/springframework/jms/support/converter/MappingJackson2MessageConverter.java +++ b/spring-jms/src/main/java/org/springframework/jms/support/converter/MappingJackson2MessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -29,12 +29,15 @@ import javax.jms.Message; import javax.jms.Session; import javax.jms.TextMessage; +import com.fasterxml.jackson.annotation.JsonView; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; import org.springframework.beans.factory.BeanClassLoaderAware; +import org.springframework.core.MethodParameter; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -50,14 +53,15 @@ import org.springframework.util.ClassUtils; * <li>{@link DeserializationFeature#FAIL_ON_UNKNOWN_PROPERTIES} is disabled</li> * </ul> * - * <p>Tested against Jackson 2.2; compatible with Jackson 2.0 and higher. + * <p>Compatible with Jackson 2.6 and higher, as of Spring 4.3. * * @author Mark Pollack * @author Dave Syer * @author Juergen Hoeller + * @author Stephane Nicoll * @since 3.1.4 */ -public class MappingJackson2MessageConverter implements MessageConverter, BeanClassLoaderAware { +public class MappingJackson2MessageConverter implements SmartMessageConverter, BeanClassLoaderAware { /** * The default encoding used for writing to text messages: UTF-8. @@ -190,6 +194,35 @@ public class MappingJackson2MessageConverter implements MessageConverter, BeanCl } @Override + public Message toMessage(Object object, Session session, Object conversionHint) + throws JMSException, MessageConversionException { + + return toMessage(object, session, getSerializationView(conversionHint)); + } + + /** + * Convert a Java object to a JMS Message using the specified json view + * and the supplied session to create the message object. + * @param object the object to convert + * @param session the Session to use for creating a JMS Message + * @param jsonView the view to use to filter the content + * @return the JMS Message + * @throws javax.jms.JMSException if thrown by JMS API methods + * @throws MessageConversionException in case of conversion failure + * @since 4.3 + */ + public Message toMessage(Object object, Session session, Class<?> jsonView) + throws JMSException, MessageConversionException { + + if (jsonView != null) { + return toMessage(object, session, this.objectMapper.writerWithView(jsonView)); + } + else { + return toMessage(object, session, this.objectMapper.writer()); + } + } + + @Override public Object fromMessage(Message message) throws JMSException, MessageConversionException { try { JavaType targetJavaType = getJavaTypeForMessage(message); @@ -200,6 +233,29 @@ public class MappingJackson2MessageConverter implements MessageConverter, BeanCl } } + protected Message toMessage(Object object, Session session, ObjectWriter objectWriter) + throws JMSException, MessageConversionException { + + Message message; + try { + switch (this.targetType) { + case TEXT: + message = mapToTextMessage(object, session, objectWriter); + break; + case BYTES: + message = mapToBytesMessage(object, session, objectWriter); + break; + default: + message = mapToMessage(object, session, objectWriter, this.targetType); + } + } + catch (IOException ex) { + throw new MessageConversionException("Could not map JSON object [" + object + "]", ex); + } + setTypeIdOnMessage(object, message); + return message; + } + /** * Map the given object to a {@link TextMessage}. @@ -210,12 +266,31 @@ public class MappingJackson2MessageConverter implements MessageConverter, BeanCl * @throws JMSException if thrown by JMS methods * @throws IOException in case of I/O errors * @see Session#createBytesMessage + * @deprecated as of 4.3, use {@link #mapToTextMessage(Object, Session, ObjectWriter)} */ + @Deprecated protected TextMessage mapToTextMessage(Object object, Session session, ObjectMapper objectMapper) throws JMSException, IOException { + return mapToTextMessage(object, session, objectMapper.writer()); + } + + /** + * Map the given object to a {@link TextMessage}. + * @param object the object to be mapped + * @param session current JMS session + * @param objectWriter the writer to use + * @return the resulting message + * @throws JMSException if thrown by JMS methods + * @throws IOException in case of I/O errors + * @see Session#createBytesMessage + * @since 4.3 + */ + protected TextMessage mapToTextMessage(Object object, Session session, ObjectWriter objectWriter) + throws JMSException, IOException { + StringWriter writer = new StringWriter(); - objectMapper.writeValue(writer, object); + objectWriter.writeValue(writer, object); return session.createTextMessage(writer.toString()); } @@ -228,13 +303,33 @@ public class MappingJackson2MessageConverter implements MessageConverter, BeanCl * @throws JMSException if thrown by JMS methods * @throws IOException in case of I/O errors * @see Session#createBytesMessage + * @deprecated as of 4.3, use {@link #mapToBytesMessage(Object, Session, ObjectWriter)} */ + @Deprecated protected BytesMessage mapToBytesMessage(Object object, Session session, ObjectMapper objectMapper) throws JMSException, IOException { + return mapToBytesMessage(object, session, objectMapper.writer()); + } + + + /** + * Map the given object to a {@link BytesMessage}. + * @param object the object to be mapped + * @param session current JMS session + * @param objectWriter the writer to use + * @return the resulting message + * @throws JMSException if thrown by JMS methods + * @throws IOException in case of I/O errors + * @since 4.3 + * @see Session#createBytesMessage + */ + protected BytesMessage mapToBytesMessage(Object object, Session session, ObjectWriter objectWriter) + throws JMSException, IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(1024); OutputStreamWriter writer = new OutputStreamWriter(bos, this.encoding); - objectMapper.writeValue(writer, object); + objectWriter.writeValue(writer, object); BytesMessage message = session.createBytesMessage(); message.writeBytes(bos.toByteArray()); @@ -256,10 +351,31 @@ public class MappingJackson2MessageConverter implements MessageConverter, BeanCl * @return the resulting message * @throws JMSException if thrown by JMS methods * @throws IOException in case of I/O errors + * @deprecated as of 4.3, use {@link #mapToMessage(Object, Session, ObjectWriter, MessageType)} */ + @Deprecated protected Message mapToMessage(Object object, Session session, ObjectMapper objectMapper, MessageType targetType) throws JMSException, IOException { + return mapToMessage(object, session, objectMapper.writer(), targetType); + } + + /** + * Template method that allows for custom message mapping. + * Invoked when {@link #setTargetType} is not {@link MessageType#TEXT} or + * {@link MessageType#BYTES}. + * <p>The default implementation throws an {@link IllegalArgumentException}. + * @param object the object to marshal + * @param session the JMS Session + * @param objectWriter the writer to use + * @param targetType the target message type (other than TEXT or BYTES) + * @return the resulting message + * @throws JMSException if thrown by JMS methods + * @throws IOException in case of I/O errors + */ + protected Message mapToMessage(Object object, Session session, ObjectWriter objectWriter, MessageType targetType) + throws JMSException, IOException { + throw new IllegalArgumentException("Unsupported message type [" + targetType + "]. MappingJackson2MessageConverter by default only supports TextMessages and BytesMessages."); } @@ -286,7 +402,6 @@ public class MappingJackson2MessageConverter implements MessageConverter, BeanCl } } - /** * Convenience method to dispatch to converters for individual message types. */ @@ -391,4 +506,42 @@ public class MappingJackson2MessageConverter implements MessageConverter, BeanCl } } + /** + * Determine a Jackson serialization view based on the given conversion hint. + * @param conversionHint the conversion hint Object as passed into the + * converter for the current conversion attempt + * @return the serialization view class, or {@code null} if none + */ + protected Class<?> getSerializationView(Object conversionHint) { + if (conversionHint instanceof MethodParameter) { + MethodParameter methodParam = (MethodParameter) conversionHint; + JsonView annotation = methodParam.getParameterAnnotation(JsonView.class); + if (annotation == null) { + annotation = methodParam.getMethodAnnotation(JsonView.class); + if (annotation == null) { + return null; + } + } + return extractViewClass(annotation, conversionHint); + } + else if (conversionHint instanceof JsonView) { + return extractViewClass((JsonView) conversionHint, conversionHint); + } + else if (conversionHint instanceof Class) { + return (Class) conversionHint; + } + else { + return null; + } + } + + private Class<?> extractViewClass(JsonView annotation, Object conversionHint) { + Class<?>[] classes = annotation.value(); + if (classes.length != 1) { + throw new IllegalArgumentException( + "@JsonView only supported for handler methods with exactly 1 class argument: " + conversionHint); + } + return classes[0]; + } + } diff --git a/spring-jms/src/main/java/org/springframework/jms/support/converter/MessagingMessageConverter.java b/spring-jms/src/main/java/org/springframework/jms/support/converter/MessagingMessageConverter.java index a4db7431..fbd1d5ad 100644 --- a/spring-jms/src/main/java/org/springframework/jms/support/converter/MessagingMessageConverter.java +++ b/spring-jms/src/main/java/org/springframework/jms/support/converter/MessagingMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -24,6 +24,8 @@ import org.springframework.beans.factory.InitializingBean; import org.springframework.jms.support.JmsHeaderMapper; import org.springframework.jms.support.SimpleJmsHeaderMapper; import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHeaders; +import org.springframework.messaging.core.AbstractMessagingTemplate; import org.springframework.messaging.support.MessageBuilder; import org.springframework.util.Assert; @@ -93,8 +95,11 @@ public class MessagingMessageConverter implements MessageConverter, Initializing Message.class.getName() + "] is handled by this converter"); } Message<?> input = (Message<?>) object; - javax.jms.Message reply = createMessageForPayload(input.getPayload(), session); - this.headerMapper.fromHeaders(input.getHeaders(), reply); + MessageHeaders headers = input.getHeaders(); + Object conversionHint = (headers != null ? headers.get( + AbstractMessagingTemplate.CONVERSION_HINT_HEADER) : null); + javax.jms.Message reply = createMessageForPayload(input.getPayload(), session, conversionHint); + this.headerMapper.fromHeaders(headers, reply); return reply; } @@ -104,7 +109,7 @@ public class MessagingMessageConverter implements MessageConverter, Initializing if (message == null) { return null; } - Map<String, Object> mappedHeaders = this.headerMapper.toHeaders(message); + Map<String, Object> mappedHeaders = extractHeaders(message); Object convertedObject = extractPayload(message); MessageBuilder<Object> builder = (convertedObject instanceof org.springframework.messaging.Message) ? MessageBuilder.fromMessage((org.springframework.messaging.Message<Object>) convertedObject) : @@ -122,9 +127,29 @@ public class MessagingMessageConverter implements MessageConverter, Initializing /** * Create a JMS message for the specified payload. * @see MessageConverter#toMessage(Object, Session) + * @deprecated as of 4.3, use {@link #createMessageForPayload(Object, Session, Object)} */ + @Deprecated protected javax.jms.Message createMessageForPayload(Object payload, Session session) throws JMSException { return this.payloadConverter.toMessage(payload, session); } + /** + * Create a JMS message for the specified payload and conversionHint. + * The conversion hint is an extra object passed to the {@link MessageConverter}, + * e.g. the associated {@code MethodParameter} (may be {@code null}}. + * @see MessageConverter#toMessage(Object, Session) + * @since 4.3 + */ + @SuppressWarnings("deprecation") + protected javax.jms.Message createMessageForPayload(Object payload, Session session, Object conversionHint) + throws JMSException { + + return createMessageForPayload(payload, session); + } + + protected final MessageHeaders extractHeaders(javax.jms.Message message) { + return this.headerMapper.toHeaders(message); + } + } diff --git a/spring-jms/src/main/java/org/springframework/jms/support/converter/SmartMessageConverter.java b/spring-jms/src/main/java/org/springframework/jms/support/converter/SmartMessageConverter.java new file mode 100644 index 00000000..2fb5cd73 --- /dev/null +++ b/spring-jms/src/main/java/org/springframework/jms/support/converter/SmartMessageConverter.java @@ -0,0 +1,51 @@ +/* + * Copyright 2002-2016 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.jms.support.converter; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Session; + +/** + * An extended {@link MessageConverter} SPI with conversion hint support. + * + * <p>In case of a conversion hint being provided, the framework will call + * the extended method if a converter implements this interface, instead + * of calling the regular {@code toMessage} variant. + * + * @author Stephane Nicoll + * @since 4.3 + */ +public interface SmartMessageConverter extends MessageConverter { + + /** + * A variant of {@link #toMessage(Object, Session)} which takes an extra conversion + * context as an argument, allowing to take e.g. annotations on a payload parameter + * into account. + * @param object the object to convert + * @param session the Session to use for creating a JMS Message + * @param conversionHint an extra object passed to the {@link MessageConverter}, + * e.g. the associated {@code MethodParameter} (may be {@code null}} + * @return the JMS Message + * @throws javax.jms.JMSException if thrown by JMS API methods + * @throws MessageConversionException in case of conversion failure + * @see #toMessage(Object, Session) + */ + Message toMessage(Object object, Session session, Object conversionHint) + throws JMSException, MessageConversionException; + +} diff --git a/spring-jms/src/main/java/org/springframework/jms/support/destination/JmsDestinationAccessor.java b/spring-jms/src/main/java/org/springframework/jms/support/destination/JmsDestinationAccessor.java index 46c0bd4c..0093f86f 100644 --- a/spring-jms/src/main/java/org/springframework/jms/support/destination/JmsDestinationAccessor.java +++ b/spring-jms/src/main/java/org/springframework/jms/support/destination/JmsDestinationAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -18,6 +18,8 @@ package org.springframework.jms.support.destination; import javax.jms.Destination; import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; import javax.jms.Session; import org.springframework.jms.support.JmsAccessor; @@ -38,6 +40,20 @@ import org.springframework.util.Assert; */ public abstract class JmsDestinationAccessor extends JmsAccessor { + /** + * Timeout value indicating that a receive operation should + * check if a message is immediately available without blocking. + * @since 4.3 + */ + public static final long RECEIVE_TIMEOUT_NO_WAIT = -1; + + /** + * Timeout value indicating a blocking receive without timeout. + * @since 4.3 + */ + public static final long RECEIVE_TIMEOUT_INDEFINITE_WAIT = 0; + + private DestinationResolver destinationResolver = new DynamicDestinationResolver(); private boolean pubSubDomain = false; @@ -98,4 +114,27 @@ public abstract class JmsDestinationAccessor extends JmsAccessor { return getDestinationResolver().resolveDestinationName(session, destinationName, isPubSubDomain()); } + /** + * Actually receive a message from the given consumer. + * @param consumer the JMS MessageConsumer to receive with + * @param timeout the receive timeout (a negative value indicates + * a no-wait receive; 0 indicates an indefinite wait attempt) + * @return the JMS Message received, or {@code null} if none + * @throws JMSException if thrown by JMS API methods + * @since 4.3 + * @see #RECEIVE_TIMEOUT_NO_WAIT + * @see #RECEIVE_TIMEOUT_INDEFINITE_WAIT + */ + protected Message receiveFromConsumer(MessageConsumer consumer, long timeout) throws JMSException { + if (timeout > 0) { + return consumer.receive(timeout); + } + else if (timeout < 0) { + return consumer.receiveNoWait(); + } + else { + return consumer.receive(); + } + } + } |