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
This commit is contained in:
parent
90e6304b49
commit
8fcbdaee24
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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;
|
||||||
import org.springframework.jms.support.destination.DynamicDestinationResolver;
|
import org.springframework.jms.support.destination.DynamicDestinationResolver;
|
||||||
import org.springframework.remoting.RemoteAccessException;
|
import org.springframework.remoting.RemoteAccessException;
|
||||||
import org.springframework.remoting.RemoteInvocationFailureException;
|
import org.springframework.remoting.RemoteInvocationFailureException;
|
||||||
|
import org.springframework.remoting.RemoteTimeoutException;
|
||||||
import org.springframework.remoting.support.DefaultRemoteInvocationFactory;
|
import org.springframework.remoting.support.DefaultRemoteInvocationFactory;
|
||||||
import org.springframework.remoting.support.RemoteInvocation;
|
import org.springframework.remoting.support.RemoteInvocation;
|
||||||
import org.springframework.remoting.support.RemoteInvocationFactory;
|
import org.springframework.remoting.support.RemoteInvocationFactory;
|
||||||
|
@ -61,6 +62,7 @@ import org.springframework.remoting.support.RemoteInvocationResult;
|
||||||
*
|
*
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @author James Strachan
|
* @author James Strachan
|
||||||
|
* @author Stephane Nicoll
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
* @see #setConnectionFactory
|
* @see #setConnectionFactory
|
||||||
* @see #setQueue
|
* @see #setQueue
|
||||||
|
@ -244,7 +246,12 @@ public class JmsInvokerClientInterceptor implements MethodInterceptor, Initializ
|
||||||
Message requestMessage = createRequestMessage(session, invocation);
|
Message requestMessage = createRequestMessage(session, invocation);
|
||||||
con.start();
|
con.start();
|
||||||
Message responseMessage = doExecuteRequest(session, queueToUse, requestMessage);
|
Message responseMessage = doExecuteRequest(session, queueToUse, requestMessage);
|
||||||
return extractInvocationResult(responseMessage);
|
if (responseMessage != null) {
|
||||||
|
return extractInvocationResult(responseMessage);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return onReceiveTimeout(invocation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
JmsUtils.closeSession(session);
|
JmsUtils.closeSession(session);
|
||||||
|
@ -362,6 +369,19 @@ public class JmsInvokerClientInterceptor implements MethodInterceptor, Initializ
|
||||||
return onInvalidResponse(responseMessage);
|
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}
|
* Callback that is invoked by {@code extractInvocationResult}
|
||||||
* when it encounters an invalid response message.
|
* when it encounters an invalid response message.
|
||||||
|
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -31,10 +31,13 @@ import javax.jms.QueueSession;
|
||||||
import javax.jms.Session;
|
import javax.jms.Session;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.ExpectedException;
|
||||||
|
|
||||||
import org.springframework.jms.support.converter.MessageConversionException;
|
import org.springframework.jms.support.converter.MessageConversionException;
|
||||||
import org.springframework.jms.support.converter.SimpleMessageConverter;
|
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.ITestBean;
|
||||||
import org.springframework.tests.sample.beans.TestBean;
|
import org.springframework.tests.sample.beans.TestBean;
|
||||||
|
|
||||||
|
@ -43,9 +46,13 @@ import static org.mockito.BDDMockito.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
|
* @author Stephane Nicoll
|
||||||
*/
|
*/
|
||||||
public class JmsInvokerTests {
|
public class JmsInvokerTests {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final ExpectedException thrown = ExpectedException.none();
|
||||||
|
|
||||||
private QueueConnectionFactory mockConnectionFactory;
|
private QueueConnectionFactory mockConnectionFactory;
|
||||||
|
|
||||||
private QueueConnection mockConnection;
|
private QueueConnection mockConnection;
|
||||||
|
@ -78,6 +85,27 @@ public class JmsInvokerTests {
|
||||||
doTestJmsInvokerProxyFactoryBeanAndServiceExporter(true);
|
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 {
|
private void doTestJmsInvokerProxyFactoryBeanAndServiceExporter(boolean dynamicQueue) throws Throwable {
|
||||||
TestBean target = new TestBean("myname", 99);
|
TestBean target = new TestBean("myname", 99);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue