Skip to content

Commit 355c6f0

Browse files
committed
Upgrade to JMS 2.0 and JCA 1.7
Issue: SPR-13793
1 parent bc2c22d commit 355c6f0

23 files changed

+381
-208
lines changed

build.gradle

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ configure(allprojects) { project ->
3232
version = qualifyVersionIfNecessary(version)
3333

3434
ext.aspectjVersion = "1.8.9"
35+
ext.beanvalVersion = "1.1.0.Final"
3536
ext.caffeineVersion = "2.3.1"
3637
ext.eclipselinkVersion = "2.6.3"
3738
ext.ehcacheVersion = "2.10.2"
@@ -52,8 +53,10 @@ configure(allprojects) { project ->
5253
ext.jackson2Version = "2.8.0"
5354
ext.jasperreportsVersion = "6.2.1" // our tests fail with JR-internal NPEs against 6.2.2 and higher
5455
ext.javamailVersion = "1.5.5"
56+
ext.jcaVersion = "1.7"
5557
ext.jettyVersion = "9.3.10.v20160621"
5658
ext.jodaVersion = "2.9.4"
59+
ext.jpaVersion = "2.1.1"
5760
ext.jtaVersion = "1.2"
5861
ext.junitVersion = "4.12"
5962
ext.junitJupiterVersion = '5.0.0-SNAPSHOT'
@@ -458,8 +461,8 @@ project("spring-context") {
458461
optional("javax.ejb:ejb-api:${ejbVersion}")
459462
optional("javax.enterprise.concurrent:javax.enterprise.concurrent-api:1.0")
460463
optional("javax.money:money-api:1.0")
461-
optional("org.eclipse.persistence:javax.persistence:2.1.1")
462-
optional("javax.validation:validation-api:1.1.0.Final")
464+
optional("org.eclipse.persistence:javax.persistence:${jpaVersion}")
465+
optional("javax.validation:validation-api:${beanvalVersion}")
463466
optional("org.hibernate:hibernate-validator:${hibval5Version}")
464467
optional("joda-time:joda-time:${jodaVersion}")
465468
optional("org.aspectj:aspectjweaver:${aspectjVersion}")
@@ -494,7 +497,7 @@ project("spring-messaging") {
494497
optional("com.fasterxml.jackson.core:jackson-databind:${jackson2Version}")
495498
testCompile("javax.inject:javax.inject-tck:1")
496499
testCompile("javax.servlet:javax.servlet-api:3.1.0")
497-
testCompile("javax.validation:validation-api:1.1.0.Final")
500+
testCompile("javax.validation:validation-api:${beanvalVersion}")
498501
testCompile("com.thoughtworks.xstream:xstream:${xstreamVersion}")
499502
testCompile("org.apache.activemq:activemq-broker:5.8.0")
500503
testCompile("org.apache.activemq:activemq-kahadb-store:5.8.0") {
@@ -521,11 +524,11 @@ project("spring-tx") {
521524
optional(project(":spring-aop"))
522525
optional(project(":spring-context")) // for JCA, @EnableTransactionManagement
523526
optional("javax.transaction:javax.transaction-api:${jtaVersion}")
524-
optional("javax.resource:connector-api:1.5")
527+
optional("javax.resource:javax.resource-api:${jcaVersion}")
525528
optional("javax.ejb:ejb-api:${ejbVersion}")
526529
optional("com.ibm.websphere:uow:6.0.2.17")
527530
testCompile("org.aspectj:aspectjweaver:${aspectjVersion}")
528-
testCompile("org.eclipse.persistence:javax.persistence:2.1.1")
531+
testCompile("org.eclipse.persistence:javax.persistence:${jpaVersion}")
529532
testCompile("org.codehaus.groovy:groovy-all:${groovyVersion}")
530533
}
531534
}
@@ -576,10 +579,10 @@ project("spring-jms") {
576579
compile(project(":spring-context"))
577580
compile(project(":spring-messaging"))
578581
compile(project(":spring-tx"))
579-
provided("javax.jms:jms-api:1.1-rev-1")
582+
provided("javax.jms:javax.jms-api:2.0.1")
580583
optional(project(":spring-oxm"))
581584
optional("javax.transaction:javax.transaction-api:${jtaVersion}")
582-
optional("javax.resource:connector-api:1.5")
585+
optional("javax.resource:javax.resource-api:${jcaVersion}")
583586
optional("com.fasterxml.jackson.core:jackson-databind:${jackson2Version}")
584587
}
585588
}
@@ -665,7 +668,7 @@ project("spring-web") {
665668
optional("javax.servlet.jsp:javax.servlet.jsp-api:2.2.1")
666669
optional("javax.el:javax.el-api:2.2.5")
667670
optional("javax.faces:javax.faces-api:2.2")
668-
optional("javax.validation:validation-api:1.1.0.Final")
671+
optional("javax.validation:validation-api:${beanvalVersion}")
669672
optional("org.codehaus.groovy:groovy-all:${groovyVersion}")
670673
optional("com.caucho:hessian:4.0.38")
671674
optional("commons-fileupload:commons-fileupload:${fileuploadVersion}")
@@ -711,7 +714,7 @@ project("spring-orm") {
711714
optional(project(":spring-aop"))
712715
optional(project(":spring-context"))
713716
optional(project(":spring-web"))
714-
optional("org.eclipse.persistence:javax.persistence:2.1.1")
717+
optional("org.eclipse.persistence:javax.persistence:${jpaVersion}")
715718
optional("org.eclipse.persistence:org.eclipse.persistence.core:${eclipselinkVersion}")
716719
optional("org.eclipse.persistence:org.eclipse.persistence.jpa:${eclipselinkVersion}") {
717720
exclude group: 'org.eclipse.persistence', module: 'javax.persistence'
@@ -790,7 +793,7 @@ project("spring-webmvc") {
790793
testCompile("org.eclipse.jetty:jetty-server:${jettyVersion}") {
791794
exclude group: "javax.servlet", module: "javax.servlet"
792795
}
793-
testCompile("javax.validation:validation-api:1.1.0.Final")
796+
testCompile("javax.validation:validation-api:${beanvalVersion}")
794797
testCompile("org.hibernate:hibernate-validator:${hibval5Version}")
795798
testCompile("org.apache.httpcomponents:httpclient:${httpclientVersion}")
796799
testCompile("commons-fileupload:commons-fileupload:${fileuploadVersion}")
@@ -949,7 +952,7 @@ project("spring-aspects") {
949952
ajc("org.aspectj:aspectjtools:${aspectjVersion}")
950953
rt("org.aspectj:aspectjrt:${aspectjVersion}")
951954
compile("org.aspectj:aspectjweaver:${aspectjVersion}")
952-
provided("org.eclipse.persistence:javax.persistence:2.1.1")
955+
provided("org.eclipse.persistence:javax.persistence:${jpaVersion}")
953956
optional(project(":spring-aop")) // for @Async support
954957
optional(project(":spring-beans")) // for @Configurable support
955958
optional(project(":spring-context")) // for @Enable* support
@@ -1069,7 +1072,7 @@ configure(rootProject) {
10691072
testCompile(project(":spring-web"))
10701073
testCompile("javax.servlet:javax.servlet-api:3.0.1")
10711074
testCompile("javax.inject:javax.inject:1")
1072-
testCompile("javax.resource:connector-api:1.5")
1075+
testCompile("javax.resource:javax.resource-api:${jcaVersion}")
10731076
testCompile("org.aspectj:aspectjweaver:${aspectjVersion}")
10741077
testCompile("org.hibernate:hibernate-core:${hibernate5Version}")
10751078
testCompile("org.hsqldb:hsqldb:${hsqldbVersion}")

spring-jms/src/main/java/org/springframework/jms/connection/CachedMessageProducer.java

Lines changed: 33 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,10 +16,7 @@
1616

1717
package org.springframework.jms.connection;
1818

19-
import java.lang.reflect.InvocationHandler;
20-
import java.lang.reflect.InvocationTargetException;
21-
import java.lang.reflect.Method;
22-
import java.lang.reflect.Proxy;
19+
import javax.jms.CompletionListener;
2320
import javax.jms.Destination;
2421
import javax.jms.JMSException;
2522
import javax.jms.Message;
@@ -29,9 +26,6 @@
2926
import javax.jms.Topic;
3027
import javax.jms.TopicPublisher;
3128

32-
import org.springframework.util.ClassUtils;
33-
import org.springframework.util.ReflectionUtils;
34-
3529
/**
3630
* JMS MessageProducer decorator that adapts calls to a shared MessageProducer
3731
* instance underneath, managing QoS settings locally within the decorator.
@@ -41,36 +35,6 @@
4135
*/
4236
class CachedMessageProducer implements MessageProducer, QueueSender, TopicPublisher {
4337

44-
// Various JMS 2.0 MessageProducer methods, if available
45-
46-
private static final Method setDeliveryDelayMethod =
47-
ClassUtils.getMethodIfAvailable(MessageProducer.class, "setDeliveryDelay", long.class);
48-
49-
private static final Method getDeliveryDelayMethod =
50-
ClassUtils.getMethodIfAvailable(MessageProducer.class, "getDeliveryDelay");
51-
52-
private static Class<?> completionListenerClass;
53-
54-
private static Method sendWithCompletionListenerMethod;
55-
56-
private static Method sendWithDestinationAndCompletionListenerMethod;
57-
58-
static {
59-
try {
60-
completionListenerClass = ClassUtils.forName(
61-
"javax.jms.CompletionListener", CachedMessageProducer.class.getClassLoader());
62-
sendWithCompletionListenerMethod = MessageProducer.class.getMethod(
63-
"send", Message.class, int.class, int.class, long.class, completionListenerClass);
64-
sendWithDestinationAndCompletionListenerMethod = MessageProducer.class.getMethod(
65-
"send", Destination.class, Message.class, int.class, int.class, long.class, completionListenerClass);
66-
}
67-
catch (Exception ex) {
68-
// No JMS 2.0 API available
69-
completionListenerClass = null;
70-
}
71-
}
72-
73-
7438
private final MessageProducer target;
7539

7640
private Boolean originalDisableMessageID;
@@ -120,15 +84,15 @@ public boolean getDisableMessageTimestamp() throws JMSException {
12084
return this.target.getDisableMessageTimestamp();
12185
}
12286

123-
public void setDeliveryDelay(long deliveryDelay) {
87+
public void setDeliveryDelay(long deliveryDelay) throws JMSException {
12488
if (this.originalDeliveryDelay == null) {
125-
this.originalDeliveryDelay = (Long) ReflectionUtils.invokeMethod(getDeliveryDelayMethod, this.target);
89+
this.originalDeliveryDelay = this.target.getDeliveryDelay();
12690
}
127-
ReflectionUtils.invokeMethod(setDeliveryDelayMethod, this.target, deliveryDelay);
91+
this.target.setDeliveryDelay(deliveryDelay);
12892
}
12993

130-
public long getDeliveryDelay() {
131-
return (Long) ReflectionUtils.invokeMethod(getDeliveryDelayMethod, this.target);
94+
public long getDeliveryDelay() throws JMSException {
95+
return this.target.getDeliveryDelay();
13296
}
13397

13498
@Override
@@ -196,6 +160,31 @@ public void send(Destination destination, Message message, int deliveryMode, int
196160
this.target.send(destination, message, deliveryMode, priority, timeToLive);
197161
}
198162

163+
@Override
164+
public void send(Message message, CompletionListener completionListener) throws JMSException {
165+
this.target.send(message, this.deliveryMode, this.priority, this.timeToLive, completionListener);
166+
}
167+
168+
@Override
169+
public void send(Message message, int deliveryMode, int priority, long timeToLive,
170+
CompletionListener completionListener) throws JMSException {
171+
172+
this.target.send(message, deliveryMode, priority, timeToLive, completionListener);
173+
}
174+
175+
@Override
176+
public void send(Destination destination, Message message, CompletionListener completionListener) throws JMSException {
177+
this.target.send(destination, message, this.deliveryMode, this.priority, this.timeToLive, completionListener);
178+
}
179+
180+
@Override
181+
public void send(Destination destination, Message message, int deliveryMode, int priority,
182+
long timeToLive, CompletionListener completionListener) throws JMSException {
183+
184+
this.target.send(destination, message, deliveryMode, priority, timeToLive, completionListener);
185+
186+
}
187+
199188
@Override
200189
public void send(Queue queue, Message message) throws JMSException {
201190
this.target.send(queue, message, this.deliveryMode, this.priority, this.timeToLive);
@@ -238,7 +227,7 @@ public void close() throws JMSException {
238227
this.originalDisableMessageTimestamp = null;
239228
}
240229
if (this.originalDeliveryDelay != null) {
241-
ReflectionUtils.invokeMethod(setDeliveryDelayMethod, this.target, this.originalDeliveryDelay);
230+
this.target.setDeliveryDelay(this.originalDeliveryDelay);
242231
this.originalDeliveryDelay = null;
243232
}
244233
}
@@ -248,54 +237,4 @@ public String toString() {
248237
return "Cached JMS MessageProducer: " + this.target;
249238
}
250239

251-
252-
/**
253-
* Build a dynamic proxy that reflectively adapts to JMS 2.0 API methods, if necessary.
254-
* Otherwise simply return this CachedMessageProducer instance itself.
255-
*/
256-
public MessageProducer getProxyIfNecessary() {
257-
if (completionListenerClass != null) {
258-
return (MessageProducer) Proxy.newProxyInstance(CachedMessageProducer.class.getClassLoader(),
259-
new Class<?>[] {MessageProducer.class, QueueSender.class, TopicPublisher.class},
260-
new Jms2MessageProducerInvocationHandler());
261-
}
262-
else {
263-
return this;
264-
}
265-
}
266-
267-
268-
/**
269-
* Reflective InvocationHandler which adapts to JMS 2.0 API methods that we
270-
* cannot statically compile against while preserving JMS 1.1 compatibility
271-
* (due to the new {@code javax.jms.CompletionListener} type in the signatures).
272-
*/
273-
private class Jms2MessageProducerInvocationHandler implements InvocationHandler {
274-
275-
@Override
276-
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
277-
try {
278-
if (method.getName().equals("send") && args != null &&
279-
completionListenerClass == method.getParameterTypes()[args.length - 1]) {
280-
switch (args.length) {
281-
case 2: // send(message, completionListener)
282-
return sendWithCompletionListenerMethod.invoke(
283-
target, args[0], deliveryMode, priority, timeToLive, args[1]);
284-
case 3: // send(destination, message, completionListener)
285-
return sendWithDestinationAndCompletionListenerMethod.invoke(
286-
target, args[0], args[1], deliveryMode, priority, timeToLive, args[2]);
287-
case 5: // send(message, deliveryMode, priority, timeToLive, completionListener)
288-
return sendWithCompletionListenerMethod.invoke(target, args);
289-
case 6: // send(destination, message, deliveryMode, priority, timeToLive, completionListener)
290-
return sendWithDestinationAndCompletionListenerMethod.invoke(target, args);
291-
}
292-
}
293-
return method.invoke(CachedMessageProducer.this, args);
294-
}
295-
catch (InvocationTargetException ex) {
296-
throw ex.getTargetException();
297-
}
298-
}
299-
}
300-
301240
}

spring-jms/src/main/java/org/springframework/jms/connection/CachingConnectionFactory.java

Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,7 @@
4040
import javax.jms.TopicSession;
4141

4242
import org.springframework.util.Assert;
43-
import org.springframework.util.ClassUtils;
4443
import org.springframework.util.ObjectUtils;
45-
import org.springframework.util.ReflectionUtils;
4644

4745
/**
4846
* {@link SingleConnectionFactory} subclass that adds {@link javax.jms.Session}
@@ -78,15 +76,6 @@
7876
*/
7977
public class CachingConnectionFactory extends SingleConnectionFactory {
8078

81-
/** The JMS 2.0 Session.createSharedConsumer method, if available */
82-
private static final Method createSharedConsumerMethod = ClassUtils.getMethodIfAvailable(
83-
Session.class, "createSharedConsumer", Topic.class, String.class, String.class);
84-
85-
/** The JMS 2.0 Session.createSharedDurableConsumer method, if available */
86-
private static final Method createSharedDurableConsumerMethod = ClassUtils.getMethodIfAvailable(
87-
Session.class, "createSharedDurableConsumer", Topic.class, String.class, String.class);
88-
89-
9079
private int sessionCacheSize = 1;
9180

9281
private boolean cacheProducers = true;
@@ -405,7 +394,7 @@ private MessageProducer getCachedProducer(Destination dest) throws JMSException
405394
}
406395
this.cachedProducers.put(cacheKey, producer);
407396
}
408-
return new CachedMessageProducer(producer).getProxyIfNecessary();
397+
return new CachedMessageProducer(producer);
409398
}
410399

411400
private MessageConsumer getCachedConsumer(
@@ -421,21 +410,9 @@ private MessageConsumer getCachedConsumer(
421410
else {
422411
if (dest instanceof Topic) {
423412
if (noLocal == null) {
424-
// createSharedConsumer((Topic) dest, subscription, selector);
425-
// createSharedDurableConsumer((Topic) dest, subscription, selector);
426-
Method method = (durable ? createSharedDurableConsumerMethod : createSharedConsumerMethod);
427-
try {
428-
consumer = (MessageConsumer) method.invoke(this.target, dest, subscription, selector);
429-
}
430-
catch (InvocationTargetException ex) {
431-
if (ex.getTargetException() instanceof JMSException) {
432-
throw (JMSException) ex.getTargetException();
433-
}
434-
ReflectionUtils.handleInvocationTargetException(ex);
435-
}
436-
catch (IllegalAccessException ex) {
437-
throw new IllegalStateException("Could not access JMS 2.0 API method: " + ex.getMessage());
438-
}
413+
consumer = (durable ?
414+
this.target.createSharedDurableConsumer((Topic) dest, subscription, selector) :
415+
this.target.createSharedConsumer((Topic) dest, subscription, selector));
439416
}
440417
else {
441418
consumer = (durable ?

0 commit comments

Comments
 (0)