summaryrefslogtreecommitdiff
path: root/spring-jms/src/main/java/org/springframework/jms
diff options
context:
space:
mode:
Diffstat (limited to 'spring-jms/src/main/java/org/springframework/jms')
-rw-r--r--spring-jms/src/main/java/org/springframework/jms/UncategorizedJmsException.java4
-rw-r--r--spring-jms/src/main/java/org/springframework/jms/annotation/EnableJms.java2
-rw-r--r--spring-jms/src/main/java/org/springframework/jms/annotation/JmsListener.java8
-rw-r--r--spring-jms/src/main/java/org/springframework/jms/annotation/JmsListenerAnnotationBeanPostProcessor.java17
-rw-r--r--spring-jms/src/main/java/org/springframework/jms/annotation/JmsListeners.java5
-rw-r--r--spring-jms/src/main/java/org/springframework/jms/config/JcaListenerContainerParser.java4
-rw-r--r--spring-jms/src/main/java/org/springframework/jms/config/MethodJmsListenerEndpoint.java44
-rw-r--r--spring-jms/src/main/java/org/springframework/jms/connection/CachingConnectionFactory.java22
-rw-r--r--spring-jms/src/main/java/org/springframework/jms/connection/SessionProxy.java2
-rw-r--r--spring-jms/src/main/java/org/springframework/jms/connection/TransactionAwareConnectionFactoryProxy.java4
-rw-r--r--spring-jms/src/main/java/org/springframework/jms/core/JmsTemplate.java45
-rw-r--r--spring-jms/src/main/java/org/springframework/jms/listener/AbstractPollingMessageListenerContainer.java10
-rw-r--r--spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java14
-rw-r--r--spring-jms/src/main/java/org/springframework/jms/listener/adapter/AbstractAdaptableMessageListener.java106
-rw-r--r--spring-jms/src/main/java/org/springframework/jms/listener/adapter/MessagingMessageListenerAdapter.java16
-rw-r--r--spring-jms/src/main/java/org/springframework/jms/listener/endpoint/JmsMessageEndpointFactory.java4
-rw-r--r--spring-jms/src/main/java/org/springframework/jms/support/JmsAccessor.java2
-rw-r--r--spring-jms/src/main/java/org/springframework/jms/support/converter/MappingJackson2MessageConverter.java165
-rw-r--r--spring-jms/src/main/java/org/springframework/jms/support/converter/MessagingMessageConverter.java33
-rw-r--r--spring-jms/src/main/java/org/springframework/jms/support/converter/SmartMessageConverter.java51
-rw-r--r--spring-jms/src/main/java/org/springframework/jms/support/destination/JmsDestinationAccessor.java41
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">
* &#064;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 &mdash; for example,
* "5-10" &mdash; or a simple upper limit String &mdash; 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();
+ }
+ }
+
}