Browse Source

Handle receive timeout in JmsInvokerClientInterceptor

JmsInvokerClientInterceptor defines a receiveTimeout field but does not
handle such timeout. This commit adds an additional callback that throws
an RemoteTimeoutException instead.

Sub-classes can override the onReceiveTimeout to throw a different
exception or return a fallback RemoteInvocationResult.

Issue: SPR-12731
pull/703/merge
Stephane Nicoll 11 years ago
parent
commit
8fcbdaee24
  1. 45
      spring-context/src/main/java/org/springframework/remoting/RemoteTimeoutException.java
  2. 24
      spring-jms/src/main/java/org/springframework/jms/remoting/JmsInvokerClientInterceptor.java
  3. 30
      spring-jms/src/test/java/org/springframework/jms/remoting/JmsInvokerTests.java

45
spring-context/src/main/java/org/springframework/remoting/RemoteTimeoutException.java

@ -0,0 +1,45 @@ @@ -0,0 +1,45 @@
/*
* Copyright 2002-2015 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.remoting;
/**
* RemoteAccessException subclass to be thrown when the execution
* of the target method did not complete before a configurable
* timeout, for example when a reply message was not received.
* @author Stephane Nicoll
* @since 4.2
*/
@SuppressWarnings("serial")
public class RemoteTimeoutException extends RemoteAccessException {
/**
* Constructor for RemoteTimeoutException.
* @param msg the detail message
*/
public RemoteTimeoutException(String msg) {
super(msg);
}
/**
* Constructor for RemoteTimeoutException.
* @param msg the detail message
* @param cause the root cause from the remoting API in use
*/
public RemoteTimeoutException(String msg, Throwable cause) {
super(msg, cause);
}
}

24
spring-jms/src/main/java/org/springframework/jms/remoting/JmsInvokerClientInterceptor.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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.
@ -40,6 +40,7 @@ import org.springframework.jms.support.destination.DestinationResolver; @@ -40,6 +40,7 @@ import org.springframework.jms.support.destination.DestinationResolver;
import org.springframework.jms.support.destination.DynamicDestinationResolver;
import org.springframework.remoting.RemoteAccessException;
import org.springframework.remoting.RemoteInvocationFailureException;
import org.springframework.remoting.RemoteTimeoutException;
import org.springframework.remoting.support.DefaultRemoteInvocationFactory;
import org.springframework.remoting.support.RemoteInvocation;
import org.springframework.remoting.support.RemoteInvocationFactory;
@ -61,6 +62,7 @@ import org.springframework.remoting.support.RemoteInvocationResult; @@ -61,6 +62,7 @@ import org.springframework.remoting.support.RemoteInvocationResult;
*
* @author Juergen Hoeller
* @author James Strachan
* @author Stephane Nicoll
* @since 2.0
* @see #setConnectionFactory
* @see #setQueue
@ -244,7 +246,12 @@ public class JmsInvokerClientInterceptor implements MethodInterceptor, Initializ @@ -244,7 +246,12 @@ public class JmsInvokerClientInterceptor implements MethodInterceptor, Initializ
Message requestMessage = createRequestMessage(session, invocation);
con.start();
Message responseMessage = doExecuteRequest(session, queueToUse, requestMessage);
return extractInvocationResult(responseMessage);
if (responseMessage != null) {
return extractInvocationResult(responseMessage);
}
else {
return onReceiveTimeout(invocation);
}
}
finally {
JmsUtils.closeSession(session);
@ -362,6 +369,19 @@ public class JmsInvokerClientInterceptor implements MethodInterceptor, Initializ @@ -362,6 +369,19 @@ public class JmsInvokerClientInterceptor implements MethodInterceptor, Initializ
return onInvalidResponse(responseMessage);
}
/**
* Callback that is invoked by {@code executeRequest} when the receive
* timeout has expired for the specified {@link RemoteInvocation}
* <p>By default, an {@link RemoteTimeoutException} is thrown. Sub-classes
* can choose to either throw a more dedicated exception or event return
* a default {@link RemoteInvocationResult} as a fallback.
* @param invocation the invocation
* @return a default result when the receive timeout has expired
*/
protected RemoteInvocationResult onReceiveTimeout(RemoteInvocation invocation) {
throw new RemoteTimeoutException("Receive timeout after " + this.receiveTimeout + " ms for " + invocation);
}
/**
* Callback that is invoked by {@code extractInvocationResult}
* when it encounters an invalid response message.

30
spring-jms/src/test/java/org/springframework/jms/remoting/JmsInvokerTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2015 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.
@ -31,10 +31,13 @@ import javax.jms.QueueSession; @@ -31,10 +31,13 @@ import javax.jms.QueueSession;
import javax.jms.Session;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.jms.support.converter.MessageConversionException;
import org.springframework.jms.support.converter.SimpleMessageConverter;
import org.springframework.remoting.RemoteTimeoutException;
import org.springframework.tests.sample.beans.ITestBean;
import org.springframework.tests.sample.beans.TestBean;
@ -43,9 +46,13 @@ import static org.mockito.BDDMockito.*; @@ -43,9 +46,13 @@ import static org.mockito.BDDMockito.*;
/**
* @author Juergen Hoeller
* @author Stephane Nicoll
*/
public class JmsInvokerTests {
@Rule
public final ExpectedException thrown = ExpectedException.none();
private QueueConnectionFactory mockConnectionFactory;
private QueueConnection mockConnection;
@ -78,6 +85,27 @@ public class JmsInvokerTests { @@ -78,6 +85,27 @@ public class JmsInvokerTests {
doTestJmsInvokerProxyFactoryBeanAndServiceExporter(true);
}
@Test
public void receiveTimeoutExpired() {
JmsInvokerProxyFactoryBean pfb = new JmsInvokerProxyFactoryBean() {
@Override
protected Message doExecuteRequest(Session session, Queue queue, Message requestMessage) throws JMSException {
return null; // faking no message received
}
};
pfb.setServiceInterface(ITestBean.class);
pfb.setConnectionFactory(this.mockConnectionFactory);
pfb.setQueue(this.mockQueue);
pfb.setReceiveTimeout(1500);
pfb.afterPropertiesSet();
ITestBean proxy = (ITestBean) pfb.getObject();
thrown.expect(RemoteTimeoutException.class);
thrown.expectMessage("1500 ms");
thrown.expectMessage("getAge");
proxy.getAge();
}
private void doTestJmsInvokerProxyFactoryBeanAndServiceExporter(boolean dynamicQueue) throws Throwable {
TestBean target = new TestBean("myname", 99);

Loading…
Cancel
Save