Upgrade to JMS 2.0 and JCA 1.7

Issue: SPR-13793
This commit is contained in:
Juergen Hoeller 2016-07-05 22:18:19 +02:00
parent bc2c22d51e
commit 355c6f0715
23 changed files with 381 additions and 208 deletions

View File

@ -32,6 +32,7 @@ configure(allprojects) { project ->
version = qualifyVersionIfNecessary(version)
ext.aspectjVersion = "1.8.9"
ext.beanvalVersion = "1.1.0.Final"
ext.caffeineVersion = "2.3.1"
ext.eclipselinkVersion = "2.6.3"
ext.ehcacheVersion = "2.10.2"
@ -52,8 +53,10 @@ configure(allprojects) { project ->
ext.jackson2Version = "2.8.0"
ext.jasperreportsVersion = "6.2.1" // our tests fail with JR-internal NPEs against 6.2.2 and higher
ext.javamailVersion = "1.5.5"
ext.jcaVersion = "1.7"
ext.jettyVersion = "9.3.10.v20160621"
ext.jodaVersion = "2.9.4"
ext.jpaVersion = "2.1.1"
ext.jtaVersion = "1.2"
ext.junitVersion = "4.12"
ext.junitJupiterVersion = '5.0.0-SNAPSHOT'
@ -458,8 +461,8 @@ project("spring-context") {
optional("javax.ejb:ejb-api:${ejbVersion}")
optional("javax.enterprise.concurrent:javax.enterprise.concurrent-api:1.0")
optional("javax.money:money-api:1.0")
optional("org.eclipse.persistence:javax.persistence:2.1.1")
optional("javax.validation:validation-api:1.1.0.Final")
optional("org.eclipse.persistence:javax.persistence:${jpaVersion}")
optional("javax.validation:validation-api:${beanvalVersion}")
optional("org.hibernate:hibernate-validator:${hibval5Version}")
optional("joda-time:joda-time:${jodaVersion}")
optional("org.aspectj:aspectjweaver:${aspectjVersion}")
@ -494,7 +497,7 @@ project("spring-messaging") {
optional("com.fasterxml.jackson.core:jackson-databind:${jackson2Version}")
testCompile("javax.inject:javax.inject-tck:1")
testCompile("javax.servlet:javax.servlet-api:3.1.0")
testCompile("javax.validation:validation-api:1.1.0.Final")
testCompile("javax.validation:validation-api:${beanvalVersion}")
testCompile("com.thoughtworks.xstream:xstream:${xstreamVersion}")
testCompile("org.apache.activemq:activemq-broker:5.8.0")
testCompile("org.apache.activemq:activemq-kahadb-store:5.8.0") {
@ -521,11 +524,11 @@ project("spring-tx") {
optional(project(":spring-aop"))
optional(project(":spring-context")) // for JCA, @EnableTransactionManagement
optional("javax.transaction:javax.transaction-api:${jtaVersion}")
optional("javax.resource:connector-api:1.5")
optional("javax.resource:javax.resource-api:${jcaVersion}")
optional("javax.ejb:ejb-api:${ejbVersion}")
optional("com.ibm.websphere:uow:6.0.2.17")
testCompile("org.aspectj:aspectjweaver:${aspectjVersion}")
testCompile("org.eclipse.persistence:javax.persistence:2.1.1")
testCompile("org.eclipse.persistence:javax.persistence:${jpaVersion}")
testCompile("org.codehaus.groovy:groovy-all:${groovyVersion}")
}
}
@ -576,10 +579,10 @@ project("spring-jms") {
compile(project(":spring-context"))
compile(project(":spring-messaging"))
compile(project(":spring-tx"))
provided("javax.jms:jms-api:1.1-rev-1")
provided("javax.jms:javax.jms-api:2.0.1")
optional(project(":spring-oxm"))
optional("javax.transaction:javax.transaction-api:${jtaVersion}")
optional("javax.resource:connector-api:1.5")
optional("javax.resource:javax.resource-api:${jcaVersion}")
optional("com.fasterxml.jackson.core:jackson-databind:${jackson2Version}")
}
}
@ -665,7 +668,7 @@ project("spring-web") {
optional("javax.servlet.jsp:javax.servlet.jsp-api:2.2.1")
optional("javax.el:javax.el-api:2.2.5")
optional("javax.faces:javax.faces-api:2.2")
optional("javax.validation:validation-api:1.1.0.Final")
optional("javax.validation:validation-api:${beanvalVersion}")
optional("org.codehaus.groovy:groovy-all:${groovyVersion}")
optional("com.caucho:hessian:4.0.38")
optional("commons-fileupload:commons-fileupload:${fileuploadVersion}")
@ -711,7 +714,7 @@ project("spring-orm") {
optional(project(":spring-aop"))
optional(project(":spring-context"))
optional(project(":spring-web"))
optional("org.eclipse.persistence:javax.persistence:2.1.1")
optional("org.eclipse.persistence:javax.persistence:${jpaVersion}")
optional("org.eclipse.persistence:org.eclipse.persistence.core:${eclipselinkVersion}")
optional("org.eclipse.persistence:org.eclipse.persistence.jpa:${eclipselinkVersion}") {
exclude group: 'org.eclipse.persistence', module: 'javax.persistence'
@ -790,7 +793,7 @@ project("spring-webmvc") {
testCompile("org.eclipse.jetty:jetty-server:${jettyVersion}") {
exclude group: "javax.servlet", module: "javax.servlet"
}
testCompile("javax.validation:validation-api:1.1.0.Final")
testCompile("javax.validation:validation-api:${beanvalVersion}")
testCompile("org.hibernate:hibernate-validator:${hibval5Version}")
testCompile("org.apache.httpcomponents:httpclient:${httpclientVersion}")
testCompile("commons-fileupload:commons-fileupload:${fileuploadVersion}")
@ -949,7 +952,7 @@ project("spring-aspects") {
ajc("org.aspectj:aspectjtools:${aspectjVersion}")
rt("org.aspectj:aspectjrt:${aspectjVersion}")
compile("org.aspectj:aspectjweaver:${aspectjVersion}")
provided("org.eclipse.persistence:javax.persistence:2.1.1")
provided("org.eclipse.persistence:javax.persistence:${jpaVersion}")
optional(project(":spring-aop")) // for @Async support
optional(project(":spring-beans")) // for @Configurable support
optional(project(":spring-context")) // for @Enable* support
@ -1069,7 +1072,7 @@ configure(rootProject) {
testCompile(project(":spring-web"))
testCompile("javax.servlet:javax.servlet-api:3.0.1")
testCompile("javax.inject:javax.inject:1")
testCompile("javax.resource:connector-api:1.5")
testCompile("javax.resource:javax.resource-api:${jcaVersion}")
testCompile("org.aspectj:aspectjweaver:${aspectjVersion}")
testCompile("org.hibernate:hibernate-core:${hibernate5Version}")
testCompile("org.hsqldb:hsqldb:${hsqldbVersion}")

View File

@ -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.
@ -16,10 +16,7 @@
package org.springframework.jms.connection;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import javax.jms.CompletionListener;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
@ -29,9 +26,6 @@ import javax.jms.QueueSender;
import javax.jms.Topic;
import javax.jms.TopicPublisher;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
* JMS MessageProducer decorator that adapts calls to a shared MessageProducer
* instance underneath, managing QoS settings locally within the decorator.
@ -41,36 +35,6 @@ import org.springframework.util.ReflectionUtils;
*/
class CachedMessageProducer implements MessageProducer, QueueSender, TopicPublisher {
// Various JMS 2.0 MessageProducer methods, if available
private static final Method setDeliveryDelayMethod =
ClassUtils.getMethodIfAvailable(MessageProducer.class, "setDeliveryDelay", long.class);
private static final Method getDeliveryDelayMethod =
ClassUtils.getMethodIfAvailable(MessageProducer.class, "getDeliveryDelay");
private static Class<?> completionListenerClass;
private static Method sendWithCompletionListenerMethod;
private static Method sendWithDestinationAndCompletionListenerMethod;
static {
try {
completionListenerClass = ClassUtils.forName(
"javax.jms.CompletionListener", CachedMessageProducer.class.getClassLoader());
sendWithCompletionListenerMethod = MessageProducer.class.getMethod(
"send", Message.class, int.class, int.class, long.class, completionListenerClass);
sendWithDestinationAndCompletionListenerMethod = MessageProducer.class.getMethod(
"send", Destination.class, Message.class, int.class, int.class, long.class, completionListenerClass);
}
catch (Exception ex) {
// No JMS 2.0 API available
completionListenerClass = null;
}
}
private final MessageProducer target;
private Boolean originalDisableMessageID;
@ -120,15 +84,15 @@ class CachedMessageProducer implements MessageProducer, QueueSender, TopicPublis
return this.target.getDisableMessageTimestamp();
}
public void setDeliveryDelay(long deliveryDelay) {
public void setDeliveryDelay(long deliveryDelay) throws JMSException {
if (this.originalDeliveryDelay == null) {
this.originalDeliveryDelay = (Long) ReflectionUtils.invokeMethod(getDeliveryDelayMethod, this.target);
this.originalDeliveryDelay = this.target.getDeliveryDelay();
}
ReflectionUtils.invokeMethod(setDeliveryDelayMethod, this.target, deliveryDelay);
this.target.setDeliveryDelay(deliveryDelay);
}
public long getDeliveryDelay() {
return (Long) ReflectionUtils.invokeMethod(getDeliveryDelayMethod, this.target);
public long getDeliveryDelay() throws JMSException {
return this.target.getDeliveryDelay();
}
@Override
@ -196,6 +160,31 @@ class CachedMessageProducer implements MessageProducer, QueueSender, TopicPublis
this.target.send(destination, message, deliveryMode, priority, timeToLive);
}
@Override
public void send(Message message, CompletionListener completionListener) throws JMSException {
this.target.send(message, this.deliveryMode, this.priority, this.timeToLive, completionListener);
}
@Override
public void send(Message message, int deliveryMode, int priority, long timeToLive,
CompletionListener completionListener) throws JMSException {
this.target.send(message, deliveryMode, priority, timeToLive, completionListener);
}
@Override
public void send(Destination destination, Message message, CompletionListener completionListener) throws JMSException {
this.target.send(destination, message, this.deliveryMode, this.priority, this.timeToLive, completionListener);
}
@Override
public void send(Destination destination, Message message, int deliveryMode, int priority,
long timeToLive, CompletionListener completionListener) throws JMSException {
this.target.send(destination, message, deliveryMode, priority, timeToLive, completionListener);
}
@Override
public void send(Queue queue, Message message) throws JMSException {
this.target.send(queue, message, this.deliveryMode, this.priority, this.timeToLive);
@ -238,7 +227,7 @@ class CachedMessageProducer implements MessageProducer, QueueSender, TopicPublis
this.originalDisableMessageTimestamp = null;
}
if (this.originalDeliveryDelay != null) {
ReflectionUtils.invokeMethod(setDeliveryDelayMethod, this.target, this.originalDeliveryDelay);
this.target.setDeliveryDelay(this.originalDeliveryDelay);
this.originalDeliveryDelay = null;
}
}
@ -248,54 +237,4 @@ class CachedMessageProducer implements MessageProducer, QueueSender, TopicPublis
return "Cached JMS MessageProducer: " + this.target;
}
/**
* Build a dynamic proxy that reflectively adapts to JMS 2.0 API methods, if necessary.
* Otherwise simply return this CachedMessageProducer instance itself.
*/
public MessageProducer getProxyIfNecessary() {
if (completionListenerClass != null) {
return (MessageProducer) Proxy.newProxyInstance(CachedMessageProducer.class.getClassLoader(),
new Class<?>[] {MessageProducer.class, QueueSender.class, TopicPublisher.class},
new Jms2MessageProducerInvocationHandler());
}
else {
return this;
}
}
/**
* Reflective InvocationHandler which adapts to JMS 2.0 API methods that we
* cannot statically compile against while preserving JMS 1.1 compatibility
* (due to the new {@code javax.jms.CompletionListener} type in the signatures).
*/
private class Jms2MessageProducerInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (method.getName().equals("send") && args != null &&
completionListenerClass == method.getParameterTypes()[args.length - 1]) {
switch (args.length) {
case 2: // send(message, completionListener)
return sendWithCompletionListenerMethod.invoke(
target, args[0], deliveryMode, priority, timeToLive, args[1]);
case 3: // send(destination, message, completionListener)
return sendWithDestinationAndCompletionListenerMethod.invoke(
target, args[0], args[1], deliveryMode, priority, timeToLive, args[2]);
case 5: // send(message, deliveryMode, priority, timeToLive, completionListener)
return sendWithCompletionListenerMethod.invoke(target, args);
case 6: // send(destination, message, deliveryMode, priority, timeToLive, completionListener)
return sendWithDestinationAndCompletionListenerMethod.invoke(target, args);
}
}
return method.invoke(CachedMessageProducer.this, args);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
}

View File

@ -40,9 +40,7 @@ import javax.jms.Topic;
import javax.jms.TopicSession;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
/**
* {@link SingleConnectionFactory} subclass that adds {@link javax.jms.Session}
@ -78,15 +76,6 @@ import org.springframework.util.ReflectionUtils;
*/
public class CachingConnectionFactory extends SingleConnectionFactory {
/** The JMS 2.0 Session.createSharedConsumer method, if available */
private static final Method createSharedConsumerMethod = ClassUtils.getMethodIfAvailable(
Session.class, "createSharedConsumer", Topic.class, String.class, String.class);
/** The JMS 2.0 Session.createSharedDurableConsumer method, if available */
private static final Method createSharedDurableConsumerMethod = ClassUtils.getMethodIfAvailable(
Session.class, "createSharedDurableConsumer", Topic.class, String.class, String.class);
private int sessionCacheSize = 1;
private boolean cacheProducers = true;
@ -405,7 +394,7 @@ public class CachingConnectionFactory extends SingleConnectionFactory {
}
this.cachedProducers.put(cacheKey, producer);
}
return new CachedMessageProducer(producer).getProxyIfNecessary();
return new CachedMessageProducer(producer);
}
private MessageConsumer getCachedConsumer(
@ -421,21 +410,9 @@ public class CachingConnectionFactory extends SingleConnectionFactory {
else {
if (dest instanceof Topic) {
if (noLocal == null) {
// createSharedConsumer((Topic) dest, subscription, selector);
// createSharedDurableConsumer((Topic) dest, subscription, selector);
Method method = (durable ? createSharedDurableConsumerMethod : createSharedConsumerMethod);
try {
consumer = (MessageConsumer) method.invoke(this.target, dest, subscription, selector);
}
catch (InvocationTargetException ex) {
if (ex.getTargetException() instanceof JMSException) {
throw (JMSException) ex.getTargetException();
}
ReflectionUtils.handleInvocationTargetException(ex);
}
catch (IllegalAccessException ex) {
throw new IllegalStateException("Could not access JMS 2.0 API method: " + ex.getMessage());
}
consumer = (durable ?
this.target.createSharedDurableConsumer((Topic) dest, subscription, selector) :
this.target.createSharedConsumer((Topic) dest, subscription, selector));
}
else {
consumer = (durable ?

View File

@ -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,7 @@ package org.springframework.jms.connection;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSContext;
import javax.jms.JMSException;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
@ -95,22 +96,22 @@ public class DelegatingConnectionFactory
@Override
public Connection createConnection() throws JMSException {
return getTargetConnectionFactory().createConnection();
return obtainTargetConnectionFactory().createConnection();
}
@Override
public Connection createConnection(String username, String password) throws JMSException {
return getTargetConnectionFactory().createConnection(username, password);
return obtainTargetConnectionFactory().createConnection(username, password);
}
@Override
public QueueConnection createQueueConnection() throws JMSException {
ConnectionFactory cf = getTargetConnectionFactory();
if (cf instanceof QueueConnectionFactory) {
return ((QueueConnectionFactory) cf).createQueueConnection();
ConnectionFactory target = obtainTargetConnectionFactory();
if (target instanceof QueueConnectionFactory) {
return ((QueueConnectionFactory) target).createQueueConnection();
}
else {
Connection con = cf.createConnection();
Connection con = target.createConnection();
if (!(con instanceof QueueConnection)) {
throw new javax.jms.IllegalStateException("'targetConnectionFactory' is not a QueueConnectionFactory");
}
@ -120,12 +121,12 @@ public class DelegatingConnectionFactory
@Override
public QueueConnection createQueueConnection(String username, String password) throws JMSException {
ConnectionFactory cf = getTargetConnectionFactory();
if (cf instanceof QueueConnectionFactory) {
return ((QueueConnectionFactory) cf).createQueueConnection(username, password);
ConnectionFactory target = obtainTargetConnectionFactory();
if (target instanceof QueueConnectionFactory) {
return ((QueueConnectionFactory) target).createQueueConnection(username, password);
}
else {
Connection con = cf.createConnection(username, password);
Connection con = target.createConnection(username, password);
if (!(con instanceof QueueConnection)) {
throw new javax.jms.IllegalStateException("'targetConnectionFactory' is not a QueueConnectionFactory");
}
@ -135,12 +136,12 @@ public class DelegatingConnectionFactory
@Override
public TopicConnection createTopicConnection() throws JMSException {
ConnectionFactory cf = getTargetConnectionFactory();
if (cf instanceof TopicConnectionFactory) {
return ((TopicConnectionFactory) cf).createTopicConnection();
ConnectionFactory target = obtainTargetConnectionFactory();
if (target instanceof TopicConnectionFactory) {
return ((TopicConnectionFactory) target).createTopicConnection();
}
else {
Connection con = cf.createConnection();
Connection con = target.createConnection();
if (!(con instanceof TopicConnection)) {
throw new javax.jms.IllegalStateException("'targetConnectionFactory' is not a TopicConnectionFactory");
}
@ -150,12 +151,12 @@ public class DelegatingConnectionFactory
@Override
public TopicConnection createTopicConnection(String username, String password) throws JMSException {
ConnectionFactory cf = getTargetConnectionFactory();
if (cf instanceof TopicConnectionFactory) {
return ((TopicConnectionFactory) cf).createTopicConnection(username, password);
ConnectionFactory target = obtainTargetConnectionFactory();
if (target instanceof TopicConnectionFactory) {
return ((TopicConnectionFactory) target).createTopicConnection(username, password);
}
else {
Connection con = cf.createConnection(username, password);
Connection con = target.createConnection(username, password);
if (!(con instanceof TopicConnection)) {
throw new javax.jms.IllegalStateException("'targetConnectionFactory' is not a TopicConnectionFactory");
}
@ -163,6 +164,32 @@ public class DelegatingConnectionFactory
}
}
@Override
public JMSContext createContext() {
return obtainTargetConnectionFactory().createContext();
}
@Override
public JMSContext createContext(String userName, String password) {
return obtainTargetConnectionFactory().createContext(userName, password);
}
@Override
public JMSContext createContext(String userName, String password, int sessionMode) {
return obtainTargetConnectionFactory().createContext(userName, password, sessionMode);
}
@Override
public JMSContext createContext(int sessionMode) {
return obtainTargetConnectionFactory().createContext(sessionMode);
}
private ConnectionFactory obtainTargetConnectionFactory() {
ConnectionFactory target = getTargetConnectionFactory();
Assert.state(target != null, "'targetConnectionFactory' is required");
return target;
}
@Override
public boolean shouldStop(Connection con) {
return this.shouldStopConnections;

View File

@ -27,6 +27,7 @@ import java.util.Set;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.ExceptionListener;
import javax.jms.JMSContext;
import javax.jms.JMSException;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
@ -270,6 +271,32 @@ public class SingleConnectionFactory implements ConnectionFactory, QueueConnecti
"SingleConnectionFactory does not support custom username and password");
}
@Override
public JMSContext createContext() {
return obtainTargetConnectionFactory().createContext();
}
@Override
public JMSContext createContext(String userName, String password) {
return obtainTargetConnectionFactory().createContext(userName, password);
}
@Override
public JMSContext createContext(String userName, String password, int sessionMode) {
return obtainTargetConnectionFactory().createContext(userName, password, sessionMode);
}
@Override
public JMSContext createContext(int sessionMode) {
return obtainTargetConnectionFactory().createContext(sessionMode);
}
private ConnectionFactory obtainTargetConnectionFactory() {
ConnectionFactory target = getTargetConnectionFactory();
Assert.state(target != null, "'targetConnectionFactory' is required");
return target;
}
/**
* Obtain an initialized shared Connection.

View File

@ -24,6 +24,7 @@ import java.util.ArrayList;
import java.util.List;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSContext;
import javax.jms.JMSException;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
@ -101,7 +102,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;
}
@ -144,50 +145,76 @@ public class TransactionAwareConnectionFactoryProxy
@Override
public Connection createConnection(String username, String password) throws JMSException {
Connection targetConnection = this.targetConnectionFactory.createConnection(username, password);
Connection targetConnection = obtainTargetConnectionFactory().createConnection(username, password);
return getTransactionAwareConnectionProxy(targetConnection);
}
@Override
public QueueConnection createQueueConnection() throws JMSException {
if (!(this.targetConnectionFactory instanceof QueueConnectionFactory)) {
ConnectionFactory target = obtainTargetConnectionFactory();
if (!(target instanceof QueueConnectionFactory)) {
throw new javax.jms.IllegalStateException("'targetConnectionFactory' is no QueueConnectionFactory");
}
QueueConnection targetConnection =
((QueueConnectionFactory) this.targetConnectionFactory).createQueueConnection();
QueueConnection targetConnection = ((QueueConnectionFactory) target).createQueueConnection();
return (QueueConnection) getTransactionAwareConnectionProxy(targetConnection);
}
@Override
public QueueConnection createQueueConnection(String username, String password) throws JMSException {
if (!(this.targetConnectionFactory instanceof QueueConnectionFactory)) {
ConnectionFactory target = obtainTargetConnectionFactory();
if (!(target instanceof QueueConnectionFactory)) {
throw new javax.jms.IllegalStateException("'targetConnectionFactory' is no QueueConnectionFactory");
}
QueueConnection targetConnection =
((QueueConnectionFactory) this.targetConnectionFactory).createQueueConnection(username, password);
QueueConnection targetConnection = ((QueueConnectionFactory) target).createQueueConnection(username, password);
return (QueueConnection) getTransactionAwareConnectionProxy(targetConnection);
}
@Override
public TopicConnection createTopicConnection() throws JMSException {
if (!(this.targetConnectionFactory instanceof TopicConnectionFactory)) {
ConnectionFactory target = obtainTargetConnectionFactory();
if (!(target instanceof TopicConnectionFactory)) {
throw new javax.jms.IllegalStateException("'targetConnectionFactory' is no TopicConnectionFactory");
}
TopicConnection targetConnection =
((TopicConnectionFactory) this.targetConnectionFactory).createTopicConnection();
TopicConnection targetConnection = ((TopicConnectionFactory) target).createTopicConnection();
return (TopicConnection) getTransactionAwareConnectionProxy(targetConnection);
}
@Override
public TopicConnection createTopicConnection(String username, String password) throws JMSException {
if (!(this.targetConnectionFactory instanceof TopicConnectionFactory)) {
ConnectionFactory target = obtainTargetConnectionFactory();
if (!(target instanceof TopicConnectionFactory)) {
throw new javax.jms.IllegalStateException("'targetConnectionFactory' is no TopicConnectionFactory");
}
TopicConnection targetConnection =
((TopicConnectionFactory) this.targetConnectionFactory).createTopicConnection(username, password);
TopicConnection targetConnection = ((TopicConnectionFactory) target).createTopicConnection(username, password);
return (TopicConnection) getTransactionAwareConnectionProxy(targetConnection);
}
@Override
public JMSContext createContext() {
return obtainTargetConnectionFactory().createContext();
}
@Override
public JMSContext createContext(String userName, String password) {
return obtainTargetConnectionFactory().createContext(userName, password);
}
@Override
public JMSContext createContext(String userName, String password, int sessionMode) {
return obtainTargetConnectionFactory().createContext(userName, password, sessionMode);
}
@Override
public JMSContext createContext(int sessionMode) {
return obtainTargetConnectionFactory().createContext(sessionMode);
}
private ConnectionFactory obtainTargetConnectionFactory() {
ConnectionFactory target = getTargetConnectionFactory();
Assert.state(target != null, "'targetConnectionFactory' is required");
return target;
}
/**
* Wrap the given Connection with a proxy that delegates every method call to it

View File

@ -18,6 +18,7 @@ package org.springframework.jms.connection;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSContext;
import javax.jms.JMSException;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
@ -173,16 +174,15 @@ public class UserCredentialsConnectionFactoryAdapter
* @see javax.jms.ConnectionFactory#createConnection()
*/
protected Connection doCreateConnection(String username, String password) throws JMSException {
Assert.state(this.targetConnectionFactory != null, "'targetConnectionFactory' is required");
ConnectionFactory target = obtainTargetConnectionFactory();
if (StringUtils.hasLength(username)) {
return this.targetConnectionFactory.createConnection(username, password);
return target.createConnection(username, password);
}
else {
return this.targetConnectionFactory.createConnection();
return target.createConnection();
}
}
/**
* Determine whether there are currently thread-bound credentials,
* using them if available, falling back to the statically specified
@ -220,11 +220,11 @@ public class UserCredentialsConnectionFactoryAdapter
* @see javax.jms.QueueConnectionFactory#createQueueConnection()
*/
protected QueueConnection doCreateQueueConnection(String username, String password) throws JMSException {
Assert.state(this.targetConnectionFactory != null, "'targetConnectionFactory' is required");
if (!(this.targetConnectionFactory instanceof QueueConnectionFactory)) {
ConnectionFactory target = obtainTargetConnectionFactory();
if (!(target instanceof QueueConnectionFactory)) {
throw new javax.jms.IllegalStateException("'targetConnectionFactory' is not a QueueConnectionFactory");
}
QueueConnectionFactory queueFactory = (QueueConnectionFactory) this.targetConnectionFactory;
QueueConnectionFactory queueFactory = (QueueConnectionFactory) target;
if (StringUtils.hasLength(username)) {
return queueFactory.createQueueConnection(username, password);
}
@ -233,7 +233,6 @@ public class UserCredentialsConnectionFactoryAdapter
}
}
/**
* Determine whether there are currently thread-bound credentials,
* using them if available, falling back to the statically specified
@ -271,11 +270,11 @@ public class UserCredentialsConnectionFactoryAdapter
* @see javax.jms.TopicConnectionFactory#createTopicConnection()
*/
protected TopicConnection doCreateTopicConnection(String username, String password) throws JMSException {
Assert.state(this.targetConnectionFactory != null, "'targetConnectionFactory' is required");
if (!(this.targetConnectionFactory instanceof TopicConnectionFactory)) {
ConnectionFactory target = obtainTargetConnectionFactory();
if (!(target instanceof TopicConnectionFactory)) {
throw new javax.jms.IllegalStateException("'targetConnectionFactory' is not a TopicConnectionFactory");
}
TopicConnectionFactory queueFactory = (TopicConnectionFactory) this.targetConnectionFactory;
TopicConnectionFactory queueFactory = (TopicConnectionFactory) target;
if (StringUtils.hasLength(username)) {
return queueFactory.createTopicConnection(username, password);
}
@ -284,6 +283,31 @@ public class UserCredentialsConnectionFactoryAdapter
}
}
@Override
public JMSContext createContext() {
return obtainTargetConnectionFactory().createContext();
}
@Override
public JMSContext createContext(String userName, String password) {
return obtainTargetConnectionFactory().createContext(userName, password);
}
@Override
public JMSContext createContext(String userName, String password, int sessionMode) {
return obtainTargetConnectionFactory().createContext(userName, password, sessionMode);
}
@Override
public JMSContext createContext(int sessionMode) {
return obtainTargetConnectionFactory().createContext(sessionMode);
}
private ConnectionFactory obtainTargetConnectionFactory() {
Assert.state(this.targetConnectionFactory != null, "'targetConnectionFactory' is required");
return this.targetConnectionFactory;
}
/**
* Inner class used as ThreadLocal value.

View File

@ -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.
@ -24,7 +24,7 @@ import javax.resource.spi.UnavailableException;
import org.springframework.jca.endpoint.AbstractMessageEndpointFactory;
/**
* JMS-specific implementation of the JCA 1.5
* JMS-specific implementation of the JCA 1.7
* {@link javax.resource.spi.endpoint.MessageEndpointFactory} interface,
* providing transaction management capabilities for a JMS listener object
* (e.g. a {@link javax.jms.MessageListener} object).
@ -61,7 +61,7 @@ public class JmsMessageEndpointFactory extends AbstractMessageEndpointFactory {
* Return the JMS MessageListener for this endpoint.
*/
protected MessageListener getMessageListener() {
return messageListener;
return this.messageListener;
}
/**

View File

@ -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.
@ -18,6 +18,7 @@ package org.springframework.jms;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSContext;
import javax.jms.JMSException;
/**
@ -37,4 +38,24 @@ public class StubConnectionFactory implements ConnectionFactory {
return null;
}
@Override
public JMSContext createContext() {
return null;
}
@Override
public JMSContext createContext(String userName, String password) {
return null;
}
@Override
public JMSContext createContext(String userName, String password, int sessionMode) {
return null;
}
@Override
public JMSContext createContext(int sessionMode) {
return null;
}
}

View File

@ -44,6 +44,8 @@ public class StubTextMessage implements TextMessage {
private String type;
private long deliveryTime;
private long timestamp = 0L;
private long expiration = 0L;
@ -152,6 +154,11 @@ public class StubTextMessage implements TextMessage {
return this.type;
}
@Override
public long getJMSDeliveryTime() throws JMSException {
return this.deliveryTime;
}
public long getLongProperty(String name) throws JMSException {
Object value = this.properties.get(name);
return (value instanceof Long) ? ((Long) value).longValue() : 0;
@ -243,6 +250,11 @@ public class StubTextMessage implements TextMessage {
this.type = type;
}
@Override
public void setJMSDeliveryTime(long deliveryTime) throws JMSException {
this.deliveryTime = deliveryTime;
}
public void setLongProperty(String name, long value) throws JMSException {
this.properties.put(name, value);
}
@ -259,5 +271,14 @@ public class StubTextMessage implements TextMessage {
this.properties.put(name, value);
}
}
@Override
public <T> T getBody(Class<T> c) throws JMSException {
return null;
}
@Override
public boolean isBodyAssignableTo(Class c) throws JMSException {
return false;
}
}

View File

@ -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.
@ -43,6 +43,16 @@ public class TestConnection implements Connection {
return null;
}
@Override
public Session createSession(int sessionMode) throws JMSException {
return null;
}
@Override
public Session createSession() throws JMSException {
return null;
}
@Override
public String getClientID() throws JMSException {
return null;
@ -91,6 +101,16 @@ public class TestConnection implements Connection {
return null;
}
@Override
public ConnectionConsumer createSharedConnectionConsumer(Topic topic, String subscriptionName, String messageSelector, ServerSessionPool sessionPool, int maxMessages) throws JMSException {
return null;
}
@Override
public ConnectionConsumer createSharedDurableConnectionConsumer(Topic topic, String subscriptionName, String messageSelector, ServerSessionPool sessionPool, int maxMessages) throws JMSException {
return null;
}
public int getStartCount() {
return startCount;

View File

@ -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,6 +19,7 @@ package org.springframework.jms.remoting;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Enumeration;
import javax.jms.CompletionListener;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
@ -214,6 +215,15 @@ public class JmsInvokerTests {
return 0;
}
@Override
public void setDeliveryDelay(long deliveryDelay) throws JMSException {
}
@Override
public long getDeliveryDelay() throws JMSException {
return 0;
}
@Override
public Destination getDestination() throws JMSException {
return null;
@ -240,6 +250,22 @@ public class JmsInvokerTests {
@Override
public void send(Destination destination, Message message, int i, int i1, long l) throws JMSException {
}
@Override
public void send(Message message, CompletionListener completionListener) throws JMSException {
}
@Override
public void send(Message message, int deliveryMode, int priority, long timeToLive, CompletionListener completionListener) throws JMSException {
}
@Override
public void send(Destination destination, Message message, CompletionListener completionListener) throws JMSException {
}
@Override
public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive, CompletionListener completionListener) throws JMSException {
}
}
@ -363,6 +389,25 @@ public class JmsInvokerTests {
public void setJMSPriority(int i) throws JMSException {
}
@Override
public long getJMSDeliveryTime() throws JMSException {
return 0;
}
@Override
public void setJMSDeliveryTime(long deliveryTime) throws JMSException {
}
@Override
public <T> T getBody(Class<T> c) throws JMSException {
return null;
}
@Override
public boolean isBodyAssignableTo(Class c) throws JMSException {
return false;
}
@Override
public void clearProperties() throws JMSException {
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 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.
@ -37,7 +37,7 @@ import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* JCA 1.5 {@link javax.resource.spi.ResourceAdapter} implementation
* JCA 1.7 {@link javax.resource.spi.ResourceAdapter} implementation
* that loads a Spring {@link org.springframework.context.ApplicationContext},
* starting and stopping Spring-managed beans as part of the ResourceAdapter's
* lifecycle.

View File

@ -1,5 +1,5 @@
/**
* Integration package that allows for deploying a Spring application context
* as a JCA 1.5 compliant RAR file.
* as a JCA 1.7 compliant RAR file.
*/
package org.springframework.jca.context;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 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.
@ -34,7 +34,7 @@ import org.springframework.transaction.jta.SimpleTransactionFactory;
import org.springframework.transaction.jta.TransactionFactory;
/**
* Abstract base implementation of the JCA 1.5/1.6/1.7
* Abstract base implementation of the JCA 1.7
* {@link javax.resource.spi.endpoint.MessageEndpointFactory} interface,
* providing transaction management capabilities as well as ClassLoader
* exposure for endpoint invocations.
@ -134,10 +134,20 @@ public abstract class AbstractMessageEndpointFactory implements MessageEndpointF
* returning the bean name as set on this MessageEndpointFactory.
* @see #setBeanName
*/
@Override
public String getActivationName() {
return this.beanName;
}
/**
* Implementation of the JCA 1.7 {@code #getEndpointClass()} method,
* returning {@code} null in order to indicate a synthetic endpoint type.
*/
@Override
public Class<?> getEndpointClass() {
return null;
}
/**
* This implementation returns {@code true} if a transaction manager
* has been specified; {@code false} otherwise.
@ -166,6 +176,7 @@ public abstract class AbstractMessageEndpointFactory implements MessageEndpointF
* <p>This implementation delegates to {@link #createEndpointInternal()},
* ignoring the specified timeout. It is only here for JCA 1.6 compliance.
*/
@Override
public MessageEndpoint createEndpoint(XAResource xaResource, long timeout) throws UnavailableException {
AbstractMessageEndpoint endpoint = createEndpointInternal();
endpoint.initXAResource(xaResource);
@ -205,7 +216,7 @@ public abstract class AbstractMessageEndpointFactory implements MessageEndpointF
* This {@code beforeDelivery} implementation starts a transaction,
* if necessary, and exposes the endpoint ClassLoader as current
* thread context ClassLoader.
* <p>Note that the JCA 1.5 specification does not require a ResourceAdapter
* <p>Note that the JCA 1.7 specification does not require a ResourceAdapter
* to call this method before invoking the concrete endpoint. If this method
* has not been called (check {@link #hasBeforeDeliveryBeenCalled()}), the
* concrete endpoint method should call {@code beforeDelivery} and its
@ -255,7 +266,7 @@ public abstract class AbstractMessageEndpointFactory implements MessageEndpointF
/**
* This {@code afterDelivery} implementation resets the thread context
* ClassLoader and completes the transaction, if any.
* <p>Note that the JCA 1.5 specification does not require a ResourceAdapter
* <p>Note that the JCA 1.7 specification does not require a ResourceAdapter
* to call this method after invoking the concrete endpoint. See the
* explanation in {@link #beforeDelivery}'s javadoc.
*/

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 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,7 +29,7 @@ import org.springframework.aop.support.DelegatingIntroductionInterceptor;
import org.springframework.util.ReflectionUtils;
/**
* Generic implementation of the JCA 1.5
* Generic implementation of the JCA 1.7
* {@link javax.resource.spi.endpoint.MessageEndpointFactory} interface,
* providing transaction management capabilities for any kind of message
* listener object (e.g. {@link javax.jms.MessageListener} objects or

View File

@ -26,7 +26,7 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.SmartLifecycle;
/**
* Generic bean that manages JCA 1.5 message endpoints within a Spring
* Generic bean that manages JCA 1.7 message endpoints within a Spring
* application context, activating and deactivating the endpoint as part
* of the application context's lifecycle.
*

View File

@ -86,7 +86,7 @@ public class LocalConnectionFactoryBean implements FactoryBean<Object>, Initiali
* by the JCA specification, analogous to a JDBC DataSource and a JPA
* EntityManagerFactory.
* <p>Note that the ManagerConnectionFactory implementation might expect
* a reference to its JCA 1.5 ResourceAdapter, expressed through the
* a reference to its JCA 1.7 ResourceAdapter, expressed through the
* {@link javax.resource.spi.ResourceAdapterAssociation} interface.
* Simply inject the corresponding ResourceAdapter instance into its
* "resourceAdapter" bean property in this case, before passing the

View File

@ -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.
@ -30,11 +30,11 @@ import org.springframework.util.Assert;
/**
* {@link org.springframework.beans.factory.FactoryBean} that bootstraps
* the specified JCA 1.5 {@link javax.resource.spi.ResourceAdapter},
* the specified JCA 1.7 {@link javax.resource.spi.ResourceAdapter},
* starting it with a local {@link javax.resource.spi.BootstrapContext}
* and exposing it for bean references. It will also stop the ResourceAdapter
* on context shutdown. This corresponds to 'non-managed' bootstrap in a
* local environment, according to the JCA 1.5 specification.
* local environment, according to the JCA 1.7 specification.
*
* <p>This is essentially an adapter for bean-style bootstrapping of a
* JCA ResourceAdapter, allowing the BootstrapContext or its elements

View File

@ -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.
@ -20,10 +20,12 @@ import java.util.Timer;
import javax.resource.spi.BootstrapContext;
import javax.resource.spi.UnavailableException;
import javax.resource.spi.XATerminator;
import javax.resource.spi.work.WorkContext;
import javax.resource.spi.work.WorkManager;
import javax.transaction.TransactionSynchronizationRegistry;
/**
* Simple implementation of the JCA 1.5 {@link javax.resource.spi.BootstrapContext}
* Simple implementation of the JCA 1.7 {@link javax.resource.spi.BootstrapContext}
* interface, used for bootstrapping a JCA ResourceAdapter in a local environment.
*
* <p>Delegates to the given WorkManager and XATerminator, if any. Creates simple
@ -40,6 +42,8 @@ public class SimpleBootstrapContext implements BootstrapContext {
private XATerminator xaTerminator;
private TransactionSynchronizationRegistry transactionSynchronizationRegistry;
/**
* Create a new SimpleBootstrapContext for the given WorkManager,
@ -60,6 +64,23 @@ public class SimpleBootstrapContext implements BootstrapContext {
this.xaTerminator = xaTerminator;
}
/**
* Create a new SimpleBootstrapContext for the given WorkManager, XATerminator
* and TransactionSynchronizationRegistry.
* @param workManager the JCA WorkManager to use (may be {@code null})
* @param xaTerminator the JCA XATerminator to use (may be {@code null})
* @param transactionSynchronizationRegistry the TransactionSynchronizationRegistry
* to use (may be {@code null})
* @since 5.0
*/
public SimpleBootstrapContext(WorkManager workManager, XATerminator xaTerminator,
TransactionSynchronizationRegistry transactionSynchronizationRegistry) {
this.workManager = workManager;
this.xaTerminator = xaTerminator;
this.transactionSynchronizationRegistry = transactionSynchronizationRegistry;
}
@Override
public WorkManager getWorkManager() {
@ -79,4 +100,14 @@ public class SimpleBootstrapContext implements BootstrapContext {
return new Timer();
}
@Override
public boolean isContextSupported(Class<? extends WorkContext> workContextClass) {
return false;
}
@Override
public TransactionSynchronizationRegistry getTransactionSynchronizationRegistry() {
return this.transactionSynchronizationRegistry;
}
}

View File

@ -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.
@ -35,7 +35,7 @@ import org.springframework.core.task.TaskTimeoutException;
import org.springframework.util.Assert;
/**
* Simple JCA 1.5 {@link javax.resource.spi.work.WorkManager} implementation that
* Simple JCA 1.7 {@link javax.resource.spi.work.WorkManager} implementation that
* delegates to a Spring {@link org.springframework.core.task.TaskExecutor}.
* Provides simple task execution including start timeouts, but without support
* for a JCA ExecutionContext (i.e. without support for imported transactions).

View File

@ -43,17 +43,17 @@ import org.springframework.util.concurrent.ListenableFutureTask;
/**
* {@link org.springframework.core.task.TaskExecutor} implementation
* that delegates to a JCA 1.5 WorkManager, implementing the
* that delegates to a JCA 1.7 WorkManager, implementing the
* {@link javax.resource.spi.work.WorkManager} interface.
*
* <p>This is mainly intended for use within a JCA ResourceAdapter implementation,
* but may also be used in a standalone environment, delegating to a locally
* embedded WorkManager implementation (such as Geronimo's).
*
* <p>Also implements the JCA 1.5 WorkManager interface itself, delegating all
* <p>Also implements the JCA 1.7 WorkManager interface itself, delegating all
* calls to the target WorkManager. Hence, a caller can choose whether it wants
* to talk to this executor through the Spring TaskExecutor interface or the
* JCA 1.5 WorkManager interface.
* WorkManager interface.
*
* <p>This adapter is also capable of obtaining a JCA WorkManager from JNDI.
* This is for example appropriate on the Geronimo application server, where
@ -159,7 +159,7 @@ public class WorkManagerTaskExecutor extends JndiLocatorSupport
}
/**
* Specify a JCA 1.5 WorkListener to apply, if any.
* Specify a JCA WorkListener to apply, if any.
* <p>This shared WorkListener instance will be passed on to the
* WorkManager by all {@link #execute} calls on this TaskExecutor.
*/

View File

@ -1,5 +1,5 @@
/**
* Convenience classes for scheduling based on the JCA 1.5 WorkManager facility,
* as supported within JCA 1.5 ResourceAdapters.
* Convenience classes for scheduling based on the JCA WorkManager facility,
* as supported within ResourceAdapters.
*/
package org.springframework.jca.work;