diff --git a/spring-integration-rsocket/src/main/java/org/springframework/integration/rsocket/config/RSocketInboundGatewayParser.java b/spring-integration-rsocket/src/main/java/org/springframework/integration/rsocket/config/RSocketInboundGatewayParser.java new file mode 100644 index 0000000000..009385fe99 --- /dev/null +++ b/spring-integration-rsocket/src/main/java/org/springframework/integration/rsocket/config/RSocketInboundGatewayParser.java @@ -0,0 +1,59 @@ +/* + * Copyright 2002-2019 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 + * + * https://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.rsocket.config; + +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.rsocket.inbound.RSocketInboundGateway; + +/** + * Parser for the <inbound-gateway/> element of the 'rsocket' namespace. + * + * @author Mark Fisher + * @author Gary Russell + */ +public class RSocketInboundGatewayParser extends AbstractInboundGatewayParser { + + @Override + protected Class getBeanClass(Element element) { + return RSocketInboundGateway.class; + } + + @Override + protected boolean isEligibleAttribute(String attributeName) { + return !attributeName.equals("path") + && !attributeName.equals("rsocket-strategies") + && !attributeName.equals("rsocket-connector") + && !attributeName.equals("request-element-type") + && super.isEligibleAttribute(attributeName); + } + + @Override + protected void doPostProcess(BeanDefinitionBuilder builder, Element element) { + builder.addConstructorArgValue(element.getAttribute("path")); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "request-element-type", + "requestElementClass"); + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "rsocket-strategies", + "rSocketStrategies"); + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "rsocket-connector", + "RSocketConnector"); + } + +} diff --git a/spring-integration-rsocket/src/main/java/org/springframework/integration/rsocket/config/RSocketNamespaceHandler.java b/spring-integration-rsocket/src/main/java/org/springframework/integration/rsocket/config/RSocketNamespaceHandler.java index a11974469d..a93edaf5d5 100644 --- a/spring-integration-rsocket/src/main/java/org/springframework/integration/rsocket/config/RSocketNamespaceHandler.java +++ b/spring-integration-rsocket/src/main/java/org/springframework/integration/rsocket/config/RSocketNamespaceHandler.java @@ -28,7 +28,8 @@ import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHa public class RSocketNamespaceHandler extends AbstractIntegrationNamespaceHandler { public void init() { - + registerBeanDefinitionParser("inbound-gateway", new RSocketInboundGatewayParser()); + registerBeanDefinitionParser("outbound-gateway", new RSocketOutboundGatewayParser()); } } diff --git a/spring-integration-rsocket/src/main/java/org/springframework/integration/rsocket/config/RSocketOutboundGatewayParser.java b/spring-integration-rsocket/src/main/java/org/springframework/integration/rsocket/config/RSocketOutboundGatewayParser.java new file mode 100644 index 0000000000..60cdb9d53c --- /dev/null +++ b/spring-integration-rsocket/src/main/java/org/springframework/integration/rsocket/config/RSocketOutboundGatewayParser.java @@ -0,0 +1,71 @@ +/* + * Copyright 2019 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 + * + * https://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.rsocket.config; + +import org.w3c.dom.Element; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.core.Conventions; +import org.springframework.integration.config.xml.AbstractConsumerEndpointParser; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.integration.rsocket.outbound.RSocketOutboundGateway; + +/** + * Parser for the 'outbound-gateway' element of the rsocket namespace. + * + * @author Artem Bilan + * + * @since 5.2 + */ +public class RSocketOutboundGatewayParser extends AbstractConsumerEndpointParser { + + @Override + protected String getInputChannelAttributeName() { + return "request-channel"; + } + + @Override + protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(RSocketOutboundGateway.class); + BeanDefinition routeExpression = + IntegrationNamespaceUtils.createExpressionDefinitionFromValueOrExpression("route", "route-expression", + parserContext, element, true); + builder.addConstructorArgValue(routeExpression); + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "client-rsocket-connector", + "clientRSocketConnector"); + populateValueOrExpressionIfAny(builder, element, parserContext, "command"); + populateValueOrExpressionIfAny(builder, element, parserContext, "publisher-element-type"); + populateValueOrExpressionIfAny(builder, element, parserContext, "expected-response-type"); + return builder; + } + + private static void populateValueOrExpressionIfAny(BeanDefinitionBuilder builder, Element element, + ParserContext parserContext, String valueAttributeName) { + + String expressionAttributeName = valueAttributeName + "-expression"; + + BeanDefinition expression = + IntegrationNamespaceUtils.createExpressionDefinitionFromValueOrExpression(valueAttributeName, + expressionAttributeName, parserContext, element, false); + if (expression != null) { + builder.addPropertyValue(Conventions.attributeNameToPropertyName(expressionAttributeName), expression); + } + } + +} diff --git a/spring-integration-rsocket/src/main/java/org/springframework/integration/rsocket/inbound/RSocketInboundGateway.java b/spring-integration-rsocket/src/main/java/org/springframework/integration/rsocket/inbound/RSocketInboundGateway.java index 86a26de8ed..3cf42a2751 100644 --- a/spring-integration-rsocket/src/main/java/org/springframework/integration/rsocket/inbound/RSocketInboundGateway.java +++ b/spring-integration-rsocket/src/main/java/org/springframework/integration/rsocket/inbound/RSocketInboundGateway.java @@ -94,7 +94,7 @@ public class RSocketInboundGateway extends MessagingGatewaySupport implements In private ResolvableType requestElementType; /** - * Instantiate based on the provided path patterns to map this endpoint for incoming RSocket requests. + * Instantiate based on the provided Ant-style path patterns to map this endpoint for incoming RSocket requests. * @param path the mapping patterns to use. */ public RSocketInboundGateway(String... path) { diff --git a/spring-integration-rsocket/src/main/resources/org/springframework/integration/rsocket/config/spring-integration-rsocket-5.2.xsd b/spring-integration-rsocket/src/main/resources/org/springframework/integration/rsocket/config/spring-integration-rsocket-5.2.xsd index 1054e74df4..8f16688f8c 100644 --- a/spring-integration-rsocket/src/main/resources/org/springframework/integration/rsocket/config/spring-integration-rsocket-5.2.xsd +++ b/spring-integration-rsocket/src/main/resources/org/springframework/integration/rsocket/config/spring-integration-rsocket-5.2.xsd @@ -1,21 +1,246 @@ + xmlns:xsd="http://www.w3.org/2001/XMLSchema" + xmlns:tool="http://www.springframework.org/schema/tool" + xmlns:integration="http://www.springframework.org/schema/integration" + targetNamespace="http://www.springframework.org/schema/integration/rsocket" + elementFormDefault="qualified" + attributeFormDefault="unqualified"> + schemaLocation="https://www.springframework.org/schema/integration/spring-integration-5.2.xsd"/> - + + Defines the configuration elements for Spring Integration's RSocket channel adapters. + + + + + + Configures a Messaging Gateway Endpoint for the + 'org.springframework.integration.rsocket.inbound.RSocketInboundGateway to receive RSocket + requests and produce RSocket responses. + + + + + + + + The comma separated Ant-style path patterns this endpoint is mapped onto. + + + + + + + + + + + + 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. + + + + + + + An 'RSocketStrategies' bean reference for encoding/decoding requests/replies. + + + + + + + + + + + + An optional 'AbstractRSocketConnector' bean reference for endpoint mapping registration. + + + + + + + + + + + + A 'Class' for a request message payload type (plain or `Publisher` element). + + + + + + + + + + + + + Configures a Consumer Endpoint for the + 'org.springframework.integration.rsocket.outbound.RSocketOutboundGateway' to send requests + over RSocket connections. + + + + + + + + + + + + + Specifies the order for invocation when this endpoint is connected as a + subscriber to a SubscribableChannel. + + + + + + + A 'route' for the target RSocket endpoint. + Mutually exclusive with 'route-expression'. + + + + + + + A SpEL expression to evaluate a 'route' for target RSocket endpoint at runtime + against request message. + Mutually exclusive with 'route'. + + + + + + + A 'Command' for RSocket request type. + Mutually exclusive with 'command-expression'. + + + + + + + + + + A SpEL expression to evaluate a 'command' for RSocket request type at runtime + against request message. + Mutually exclusive with 'command'. + + + + + + + A 'Class' for a request message payload 'Publisher' type. + Mutually exclusive with 'publisher-element-type-expression'. + + + + + + + A SpEL expression to evaluate a 'Class' or 'ParameterizedTypeReference' + for a request message payload 'Publisher' type at runtime + against request message. + Mutually exclusive with 'publisher-element-type'. + + + + + + + A 'Class' for an RSocket response. + Mutually exclusive with 'expected-response-type-expression'. + + + + + + + A SpEL expression to evaluate a 'Class' or 'ParameterizedTypeReference' + for for an RSocket response at runtime + against request message. + Mutually exclusive with 'expected-response-type'. + + + + + + + A 'ClientRSocketConnector' for client side requests. + + + + + + + + + + + + + + + + + Defines common configuration for gateway adapters. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-rsocket/src/test/java/org/springframework/integration/rsocket/config/RSocketInboundGatewayParserTests-context.xml b/spring-integration-rsocket/src/test/java/org/springframework/integration/rsocket/config/RSocketInboundGatewayParserTests-context.xml new file mode 100644 index 0000000000..d8f13b7c7d --- /dev/null +++ b/spring-integration-rsocket/src/test/java/org/springframework/integration/rsocket/config/RSocketInboundGatewayParserTests-context.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + diff --git a/spring-integration-rsocket/src/test/java/org/springframework/integration/rsocket/config/RSocketInboundGatewayParserTests.java b/spring-integration-rsocket/src/test/java/org/springframework/integration/rsocket/config/RSocketInboundGatewayParserTests.java new file mode 100644 index 0000000000..cf4cbf90c9 --- /dev/null +++ b/spring-integration-rsocket/src/test/java/org/springframework/integration/rsocket/config/RSocketInboundGatewayParserTests.java @@ -0,0 +1,57 @@ +/* + * Copyright 2019 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 + * + * https://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.rsocket.config; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.rsocket.ClientRSocketConnector; +import org.springframework.integration.rsocket.inbound.RSocketInboundGateway; +import org.springframework.integration.test.util.TestUtils; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +/** + * @author Artem Bilan + * + * @since 5.2 + */ +@SpringJUnitConfig +@DirtiesContext +public class RSocketInboundGatewayParserTests { + + @Autowired + private ClientRSocketConnector clientRSocketConnector; + + @Autowired + private RSocketInboundGateway inboundGateway; + + @Test + void testOutboundGatewayParser() { + assertThat(TestUtils.getPropertyValue(this.inboundGateway, "rsocketConnector")) + .isSameAs(this.clientRSocketConnector); + assertThat(TestUtils.getPropertyValue(this.inboundGateway, "rsocketStrategies")) + .isSameAs(this.clientRSocketConnector.getRSocketStrategies()); + assertThat(TestUtils.getPropertyValue(this.inboundGateway, "path")) + .isEqualTo(new String[] { "testPath" }); + assertThat(TestUtils.getPropertyValue(this.inboundGateway, "requestElementType.resolved")) + .isEqualTo(byte[].class); + } + +} diff --git a/spring-integration-rsocket/src/test/java/org/springframework/integration/rsocket/config/RSocketOutboundGatewayParserTests-context.xml b/spring-integration-rsocket/src/test/java/org/springframework/integration/rsocket/config/RSocketOutboundGatewayParserTests-context.xml new file mode 100644 index 0000000000..61606b4be4 --- /dev/null +++ b/spring-integration-rsocket/src/test/java/org/springframework/integration/rsocket/config/RSocketOutboundGatewayParserTests-context.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + diff --git a/spring-integration-rsocket/src/test/java/org/springframework/integration/rsocket/config/RSocketOutboundGatewayParserTests.java b/spring-integration-rsocket/src/test/java/org/springframework/integration/rsocket/config/RSocketOutboundGatewayParserTests.java new file mode 100644 index 0000000000..ce6ff83107 --- /dev/null +++ b/spring-integration-rsocket/src/test/java/org/springframework/integration/rsocket/config/RSocketOutboundGatewayParserTests.java @@ -0,0 +1,59 @@ +/* + * Copyright 2019 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 + * + * https://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.rsocket.config; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.rsocket.ClientRSocketConnector; +import org.springframework.integration.rsocket.outbound.RSocketOutboundGateway; +import org.springframework.integration.test.util.TestUtils; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +/** + * @author Artem Bilan + * + * @since 5.2 + */ +@SpringJUnitConfig +@DirtiesContext +public class RSocketOutboundGatewayParserTests { + + @Autowired + private ClientRSocketConnector clientRSocketConnector; + + @Autowired + private RSocketOutboundGateway outboundGateway; + + @Test + void testOutboundGatewayParser() { + assertThat(TestUtils.getPropertyValue(this.outboundGateway, "clientRSocketConnector")) + .isSameAs(this.clientRSocketConnector); + assertThat(TestUtils.getPropertyValue(this.outboundGateway, "commandExpression.literalValue")) + .isEqualTo("fireAndForget"); + assertThat(TestUtils.getPropertyValue(this.outboundGateway, "routeExpression.expression")) + .isEqualTo("'testRoute'"); + assertThat(TestUtils.getPropertyValue(this.outboundGateway, "publisherElementTypeExpression.literalValue")) + .isEqualTo("byte[]"); + assertThat(TestUtils.getPropertyValue(this.outboundGateway, "expectedResponseTypeExpression.literalValue")) + .isEqualTo("java.util.Date"); + } + +}