INT-3030 Add Error Channel to RMI Inbound Gateway

INT-3030 Polishing; Docs, Tests

Add test cases for error channel support.

Dynamically discover available RMI port.

Add What's New and `<important/>` to reference.
This commit is contained in:
Gary Russell
2013-05-23 17:51:04 -04:00
parent ef3feb687b
commit 54e24ce97d
10 changed files with 195 additions and 10 deletions

View File

@@ -644,7 +644,7 @@
<xsd:attribute name="error-channel" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Identifies channel that error messages will be sent to if a failure occurs in this
Identifies a channel that error messages will be sent to if a failure occurs in this
gateway's invocation. If no "error-channel" reference is provided, this gateway will
propagate Exceptions to the caller. To completely suppress Exceptions, provide a
reference to the "nullChannel" here.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2013 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.
@@ -21,11 +21,13 @@ import org.w3c.dom.Element;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.integration.config.xml.AbstractInboundGatewayParser;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.integration.rmi.RmiInboundGateway;
/**
* Parser for the &lt;inbound-gateway/&gt; element of the 'rmi' namespace.
*
* Parser for the &lt;inbound-gateway/&gt; element of the 'rmi' namespace.
*
* @author Mark Fisher
* @author Gary Russell
*/
public class RmiInboundGatewayParser extends AbstractInboundGatewayParser {
@@ -34,7 +36,7 @@ public class RmiInboundGatewayParser extends AbstractInboundGatewayParser {
@Override
protected String getBeanClassName(Element element) {
return "org.springframework.integration.rmi.RmiInboundGateway";
return RmiInboundGateway.class.getName();
}
@Override

View File

@@ -49,6 +49,20 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="error-channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
If a downstream exception is thrown and an error-channel is specified,
the MessagingException will be sent to this channel. Otherwise, any such exception
will be propagated to the calling system.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-rmi="http://www.springframework.org/schema/integration/rmi"
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/rmi http://www.springframework.org/schema/integration/rmi/spring-integration-rmi.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Good -->
<int:channel id="good" />
<int-rmi:outbound-gateway remote-channel="foo" host="localhost"
request-channel="good" reply-channel="reply"
port="#{@port}"/>
<int-rmi:inbound-gateway request-channel="foo" registry-port="#{@port}" />
<int:service-activator input-channel="foo" expression="'reply:' + payload" />
<int:channel id="reply">
<int:queue/>
</int:channel>
<bean id="port" class="java.lang.Integer">
<constructor-arg value="#{T(org.springframework.integration.test.util.SocketUtils).findAvailableServerSocket(11099)}" />
</bean>
<!-- Bad -->
<int:channel id="bad" />
<int-rmi:outbound-gateway remote-channel="bar" host="localhost"
request-channel="bad" reply-channel="reply"
port="#{@port}"/>
<int-rmi:inbound-gateway request-channel="bar" registry-port="#{@port}"
error-channel="errors" />
<int:channel id="bar" /> <!-- Dispatcher has no subscribers to error channel -->
<int:service-activator input-channel="errors" expression="'error:' + payload.failedMessage.payload" />
<!-- Ugly -->
<int:channel id="ugly" />
<int-rmi:outbound-gateway remote-channel="baz" host="localhost"
request-channel="ugly" reply-channel="reply"
port="#{@port}"/>
<int-rmi:inbound-gateway request-channel="baz" registry-port="#{@port}" />
<int:channel id="baz" /> <!-- Dispatcher has no subscribers no error channel -->
</beans>

View File

@@ -0,0 +1,86 @@
/*
* Copyright 2013 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.integration.rmi;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.integration.Message;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.core.SubscribableChannel;
import org.springframework.integration.message.GenericMessage;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Gary Russell
* @since 3.0
*
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class BackToBackTests {
@Autowired
private SubscribableChannel good;
@Autowired
private SubscribableChannel bad;
@Autowired
private SubscribableChannel ugly;
@Autowired
private PollableChannel reply;
@Autowired
private AbstractApplicationContext context;
@Test
public void testGood() {
good.send(new GenericMessage<String>("foo"));
Message<?> reply = this.reply.receive(0);
assertNotNull(reply);
assertEquals("reply:foo", reply.getPayload());
}
@Test
public void testBad() {
bad.send(new GenericMessage<String>("foo"));
Message<?> reply = this.reply.receive(0);
assertNotNull(reply);
assertEquals("error:foo", reply.getPayload());
}
@Test
public void testUgly() {
context.setId("context");
try {
ugly.send(new GenericMessage<String>("foo"));
fail("Expected exception");
}
catch (Exception e) {
assertEquals("Dispatcher has no subscribers for channel 'context.baz'.", e.getCause().getMessage());
}
}
}

View File

@@ -35,6 +35,7 @@ import org.springframework.integration.gateway.RequestReplyExchanger;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.SocketUtils;
import org.springframework.remoting.RemoteLookupFailureException;
import org.springframework.remoting.rmi.RmiServiceExporter;
@@ -44,7 +45,9 @@ import org.springframework.remoting.rmi.RmiServiceExporter;
*/
public class RmiOutboundGatewayTests {
private final RmiOutboundGateway gateway = new RmiOutboundGateway("rmi://localhost:1099/testRemoteHandler");
private final static int port = SocketUtils.findAvailableServerSocket(11099);
private final RmiOutboundGateway gateway = new RmiOutboundGateway("rmi://localhost:" + port + "/testRemoteHandler");
private final QueueChannel output = new QueueChannel(1);
@@ -59,6 +62,7 @@ public class RmiOutboundGatewayTests {
exporter.setService(new TestExchanger());
exporter.setServiceInterface(RequestReplyExchanger.class);
exporter.setServiceName("testRemoteHandler");
exporter.setRegistryPort(port);
exporter.afterPropertiesSet();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2013 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.integration.rmi.config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import org.junit.Test;
@@ -27,9 +28,11 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.core.MessagingTemplate;
import org.springframework.integration.rmi.RmiInboundGateway;
import org.springframework.integration.test.util.TestUtils;
/**
* @author Mark Fisher
* @author Gary Russell
*/
public class RmiInboundGatewayParserTests {
@@ -71,9 +74,11 @@ public class RmiInboundGatewayParserTests {
public void gatewayWithHost() {
ApplicationContext context = new ClassPathXmlApplicationContext(
"rmiInboundGatewayParserTests.xml", this.getClass());
RmiInboundGateway gateway = (RmiInboundGateway) context.getBean("gatewayWithHost");
RmiInboundGateway gateway = (RmiInboundGateway) context.getBean("gatewayWithHostAndErrorChannel");
DirectFieldAccessor accessor = new DirectFieldAccessor(gateway);
assertEquals("localhost", accessor.getPropertyValue("registryHost"));
assertSame(context.getBean("testErrorChannel"),
TestUtils.getPropertyValue(gateway, "errorChannel"));
}
@Test

View File

@@ -14,12 +14,15 @@
<channel id="testChannel"/>
<channel id="testErrorChannel"/>
<rmi:inbound-gateway id="gatewayWithDefaults" request-channel="testChannel"/>
<rmi:inbound-gateway id="gatewayWithCustomProperties" request-channel="testChannel"
expect-reply="false" request-timeout="123" reply-timeout="456"/>
<rmi:inbound-gateway id="gatewayWithHost" request-channel="testChannel" registry-host="localhost"/>
<rmi:inbound-gateway id="gatewayWithHostAndErrorChannel" request-channel="testChannel" registry-host="localhost"
error-channel="testErrorChannel"/>
<rmi:inbound-gateway id="gatewayWithPort" request-channel="testChannel" registry-port="1234"/>

View File

@@ -32,6 +32,13 @@
<property name="requestChannel" value="requests"/>
</bean>]]></programlisting>
</para>
<important>
If you use an <code>errorChannel</code> on an inbound gateway, it would be normal for the error flow to return a result
(or throw an exception). This is because it is likely that there is a corresponding outbound gateway waiting for a
response of some kind. Consuming a message on the error flow, and not replying, will result in no reply at the inbound
gateway. Exceptions (on the main flow when there is no errorChannel, or on the error flow) will be propagated to the
corresponding inbound gateway.
</important>
</section>
<section id="rmi-namespace">
@@ -47,7 +54,7 @@
registry-host="localhost"/>
<int-rmi:inbound-gateway id="gatewayWithPort" request-channel="testChannel"
registry-port="1234"/>
registry-port="1234" error-channel="rmiErrorChannel"/>
<int-rmi:inbound-gateway id="gatewayWithExecutorRef" request-channel="testChannel"
remote-invocation-executor="invocationExecutor"/>]]></programlisting>

View File

@@ -201,6 +201,13 @@
in addition to several other container attributes that were already available.
</para>
</section>
<section id="3.0-rmi-ec">
<title>RMI Inbound Gateway</title>
<para>
The RMI Inbound Gateway now supports an <code>error-channel</code> attribute. See
<xref linkend="rmi-inbound"/>.
</para>
</section>
<section id="3.0-stored-proc-sql-return-type">
<title>SqlReturnType support for Stored Procedure components</title>
<para>