This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/master by this push: new 7aea258 CAMEL-15731: Add support for InOnly exchange pattern to camel-mllp (#4552) 7aea258 is described below commit 7aea258261fcf791227d3872faf44d6f017ab4d9 Author: rafaljaw <42240185+rafal...@users.noreply.github.com> AuthorDate: Tue Nov 3 07:24:14 2020 +0100 CAMEL-15731: Add support for InOnly exchange pattern to camel-mllp (#4552) * CAMEL-15731: Add support for InOnly exchange pattern to camel-mllp * CAMEL-15731: Code review changes * CAMEL-15731: Regenerate documentation for camel-mllp Co-authored-by: Rafal Jaworski <rafal.jawor...@inveox.com> --- .../apache/camel/catalog/docs/mllp-component.adoc | 7 +- .../camel-mllp/src/main/docs/mllp-component.adoc | 7 +- .../component/mllp/MllpTcpClientProducer.java | 7 +- .../component/mllp/MllpTcpServerConsumer.java | 35 ++++---- .../MllpProducerConsumerLoopbackInOnlyTest.java | 91 +++++++++++++++++++ ...wledgementWithBridgeErrorHandlerInOnlyTest.java | 100 +++++++++++++++++++++ ...cpServerConsumerAcknowledgementTestSupport.java | 11 ++- .../modules/ROOT/pages/mllp-component.adoc | 7 +- 8 files changed, 239 insertions(+), 26 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/mllp-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/mllp-component.adoc index a0a558d..099ff34 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/mllp-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/mllp-component.adoc @@ -146,7 +146,9 @@ HL7 Acknowledgement (HL7 Application Acknowledgements only - AA, AE and AR), or the acknowledgement can be specified using the CamelMllpAcknowledgement exchange property. Additionally, the type of acknowledgement that will be generated can be controlled by setting -the CamelMllpAcknowledgementType exchange property. +the CamelMllpAcknowledgementType exchange property. The MLLP Consumer +can read messages without sending any HL7 Acknowledgement if the +automatic acknowledgement is disabled and exchange pattern is InOnly. == *Message Headers* @@ -204,7 +206,8 @@ The MLLP Producer supports sending MLLP-framed messages and receiving HL7 Acknowledgements. The MLLP Producer interrogates the HL7 Acknowledgments and raises exceptions if a negative acknowledgement is received. The received acknowledgement is interrogated and an exception -is raised in the event of a negative acknowledgement. +is raised in the event of a negative acknowledgement. The MLLP Producer +can ignore acknowledgements when configured with InOnly exchange pattern. == *Message Headers* diff --git a/components/camel-mllp/src/main/docs/mllp-component.adoc b/components/camel-mllp/src/main/docs/mllp-component.adoc index a0a558d..099ff34 100644 --- a/components/camel-mllp/src/main/docs/mllp-component.adoc +++ b/components/camel-mllp/src/main/docs/mllp-component.adoc @@ -146,7 +146,9 @@ HL7 Acknowledgement (HL7 Application Acknowledgements only - AA, AE and AR), or the acknowledgement can be specified using the CamelMllpAcknowledgement exchange property. Additionally, the type of acknowledgement that will be generated can be controlled by setting -the CamelMllpAcknowledgementType exchange property. +the CamelMllpAcknowledgementType exchange property. The MLLP Consumer +can read messages without sending any HL7 Acknowledgement if the +automatic acknowledgement is disabled and exchange pattern is InOnly. == *Message Headers* @@ -204,7 +206,8 @@ The MLLP Producer supports sending MLLP-framed messages and receiving HL7 Acknowledgements. The MLLP Producer interrogates the HL7 Acknowledgments and raises exceptions if a negative acknowledgement is received. The received acknowledgement is interrogated and an exception -is raised in the event of a negative acknowledgement. +is raised in the event of a negative acknowledgement. The MLLP Producer +can ignore acknowledgements when configured with InOnly exchange pattern. == *Message Headers* diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpTcpClientProducer.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpTcpClientProducer.java index c247ee7..160ee6a 100644 --- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpTcpClientProducer.java +++ b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpTcpClientProducer.java @@ -29,6 +29,7 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import org.apache.camel.Exchange; +import org.apache.camel.ExchangePattern; import org.apache.camel.Message; import org.apache.camel.api.management.ManagedAttribute; import org.apache.camel.api.management.ManagedOperation; @@ -193,7 +194,11 @@ public class MllpTcpClientProducer extends DefaultProducer implements Runnable { mllpBuffer.resetSocket(socket); } } - + if (getConfiguration().getExchangePattern() == ExchangePattern.InOnly) { + log.debug("process({}) [{}] - not checking acknowledgement from external system", + exchange.getExchangeId(), socket); + return; + } if (exchange.getException() == null) { log.debug("process({}) [{}] - reading acknowledgement from external system", exchange.getExchangeId(), socket); try { diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpTcpServerConsumer.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpTcpServerConsumer.java index 0702678..d43c356 100644 --- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpTcpServerConsumer.java +++ b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpTcpServerConsumer.java @@ -411,22 +411,25 @@ public class MllpTcpServerConsumer extends DefaultConsumer { boolean autoAck = exchange.getProperty(MllpConstants.MLLP_AUTO_ACKNOWLEDGE, true, boolean.class); if (!autoAck) { - Object acknowledgementBytesProperty = exchange.getProperty(MllpConstants.MLLP_ACKNOWLEDGEMENT); - Object acknowledgementStringProperty = exchange.getProperty(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING); - final String exceptionMessage = (acknowledgementBytesProperty == null && acknowledgementStringProperty == null) - ? "Automatic Acknowledgement is disabled and the " - + MllpConstants.MLLP_ACKNOWLEDGEMENT + " and " + MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING - + " exchange properties are null" - : "Automatic Acknowledgement is disabled and neither the " - + MllpConstants.MLLP_ACKNOWLEDGEMENT + "(type = " - + acknowledgementBytesProperty.getClass().getSimpleName() + ") nor the" - + MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING + "(type = " - + acknowledgementBytesProperty.getClass().getSimpleName() - + ") exchange properties can be converted to byte[]"; - MllpInvalidAcknowledgementException invalidAckEx = new MllpInvalidAcknowledgementException( - exceptionMessage, originalHl7MessageBytes, acknowledgementMessageBytes); - exchange.setProperty(MllpConstants.MLLP_ACKNOWLEDGEMENT_EXCEPTION, invalidAckEx); - getExceptionHandler().handleException(invalidAckEx); + if (getConfiguration().getExchangePattern() == ExchangePattern.InOut) { + Object acknowledgementBytesProperty = exchange.getProperty(MllpConstants.MLLP_ACKNOWLEDGEMENT); + Object acknowledgementStringProperty = exchange.getProperty(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING); + final String exceptionMessage + = (acknowledgementBytesProperty == null && acknowledgementStringProperty == null) + ? "Automatic Acknowledgement is disabled and the " + + MllpConstants.MLLP_ACKNOWLEDGEMENT + " and " + MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING + + " exchange properties are null" + : "Automatic Acknowledgement is disabled and neither the " + + MllpConstants.MLLP_ACKNOWLEDGEMENT + "(type = " + + acknowledgementBytesProperty.getClass().getSimpleName() + ") nor the" + + MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING + "(type = " + + acknowledgementBytesProperty.getClass().getSimpleName() + + ") exchange properties can be converted to byte[]"; + MllpInvalidAcknowledgementException invalidAckEx = new MllpInvalidAcknowledgementException( + exceptionMessage, originalHl7MessageBytes, acknowledgementMessageBytes); + exchange.setProperty(MllpConstants.MLLP_ACKNOWLEDGEMENT_EXCEPTION, invalidAckEx); + getExceptionHandler().handleException(invalidAckEx); + } } else { String acknowledgmentTypeProperty = exchange.getProperty(MllpConstants.MLLP_ACKNOWLEDGEMENT_TYPE, String.class); String msa3 = exchange.getProperty(MllpConstants.MLLP_ACKNOWLEDGEMENT_MSA_TEXT, String.class); diff --git a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpProducerConsumerLoopbackInOnlyTest.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpProducerConsumerLoopbackInOnlyTest.java new file mode 100644 index 0000000..cc30aa8 --- /dev/null +++ b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpProducerConsumerLoopbackInOnlyTest.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.camel.component.mllp; + +import java.util.concurrent.TimeUnit; + +import org.apache.camel.CamelContext; +import org.apache.camel.EndpointInject; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.impl.DefaultCamelContext; +import org.apache.camel.test.AvailablePortFinder; +import org.apache.camel.test.junit5.CamelTestSupport; +import org.apache.camel.test.mllp.Hl7TestMessageGenerator; +import org.hamcrest.CoreMatchers; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; + +public class MllpProducerConsumerLoopbackInOnlyTest extends CamelTestSupport { + + @EndpointInject("direct://source") + ProducerTemplate source; + + @EndpointInject("mock://received-and-processed") + MockEndpoint receivedAndProcessed; + + @Override + protected CamelContext createCamelContext() throws Exception { + DefaultCamelContext context = (DefaultCamelContext) super.createCamelContext(); + + context.setUseMDCLogging(false); + context.setName(this.getClass().getSimpleName()); + + return context; + } + + @Override + protected RouteBuilder[] createRouteBuilders() throws Exception { + String mllpHost = "localhost"; + int mllpPort = AvailablePortFinder.getNextAvailable(); + + return new RouteBuilder[] { + new RouteBuilder() { + + @Override + public void configure() { + fromF("mllp://%s:%d?autoAck=false&exchangePattern=InOnly", mllpHost, mllpPort) + .convertBodyTo(String.class) + .to(receivedAndProcessed); + } + }, + + new RouteBuilder() { + + @Override + public void configure() { + from(source.getDefaultEndpoint()) + .toF("mllp://%s:%d?exchangePattern=InOnly", mllpHost, mllpPort) + .setBody(header(MllpConstants.MLLP_ACKNOWLEDGEMENT)); + } + } + }; + } + + @Test + public void testLoopbackWithOneMessage() throws Exception { + String testMessage = Hl7TestMessageGenerator.generateMessage(); + receivedAndProcessed.expectedBodiesReceived(testMessage); + + String acknowledgement = source.requestBody((Object) testMessage, String.class); + assertThat("Should receive no acknowledgment for message 1", acknowledgement, CoreMatchers.nullValue()); + + assertMockEndpointsSatisfied(60, TimeUnit.SECONDS); + } +} diff --git a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpTcpServerConsumerManualAcknowledgementWithBridgeErrorHandlerInOnlyTest.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpTcpServerConsumerManualAcknowledgementWithBridgeErrorHandlerInOnlyTest.java new file mode 100644 index 0000000..a9e4a91 --- /dev/null +++ b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpTcpServerConsumerManualAcknowledgementWithBridgeErrorHandlerInOnlyTest.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.camel.component.mllp; + +import org.apache.camel.Exchange; +import org.apache.camel.ExchangePattern; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNull; + +public class MllpTcpServerConsumerManualAcknowledgementWithBridgeErrorHandlerInOnlyTest + extends TcpServerConsumerAcknowledgementTestSupport { + + @Override + protected boolean isBridgeErrorHandler() { + return true; + } + + @Override + protected boolean isAutoAck() { + return false; + } + + @Override + protected ExchangePattern exchangePattern() { + return ExchangePattern.InOnly; + } + + @Test + public void testReceiveSingleMessage() throws Exception { + result.expectedBodiesReceived(TEST_MESSAGE); + complete.expectedBodiesReceived(TEST_MESSAGE); + + receiveSingleMessage(); + + Exchange completeExchange = complete.getReceivedExchanges().get(0); + + assertNull(completeExchange.getIn().getHeader(MllpConstants.MLLP_ACKNOWLEDGEMENT)); + assertNull(completeExchange.getIn().getHeader(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING)); + } + + @Test + public void testUnparsableMessage() throws Exception { + final String testMessage = "MSH" + TEST_MESSAGE; + + result.expectedBodiesReceived(testMessage); + complete.expectedMessageCount(1); + + unparsableMessage(testMessage); + + assertNull(result.getReceivedExchanges().get(0).getProperty(Exchange.EXCEPTION_CAUGHT), + "Should not have the exception in the exchange property"); + assertNull(complete.getReceivedExchanges().get(0).getProperty(Exchange.EXCEPTION_CAUGHT), + "Should not have the exception in the exchange property"); + } + + @Test + public void testMessageWithEmptySegment() throws Exception { + final String testMessage = TEST_MESSAGE.replace("\rPID|", "\r\rPID|"); + + result.expectedBodiesReceived(testMessage); + complete.expectedMessageCount(1); + + unparsableMessage(testMessage); + + assertNull(result.getReceivedExchanges().get(0).getProperty(Exchange.EXCEPTION_CAUGHT), + "Should not have the exception in the exchange property"); + assertNull(complete.getReceivedExchanges().get(0).getProperty(Exchange.EXCEPTION_CAUGHT), + "Should not have the exception in the exchange property"); + } + + @Test + public void testMessageWithEmbeddedNewlines() throws Exception { + final String testMessage = TEST_MESSAGE.replace("\rPID|", "\r\n\rPID|\n"); + + result.expectedBodiesReceived(testMessage); + complete.expectedMessageCount(1); + + unparsableMessage(testMessage); + + assertNull(result.getReceivedExchanges().get(0).getProperty(Exchange.EXCEPTION_CAUGHT), + "Should not have the exception in the exchange property"); + assertNull(complete.getReceivedExchanges().get(0).getProperty(Exchange.EXCEPTION_CAUGHT), + "Should not have the exception in the exchange property"); + } +} diff --git a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/TcpServerConsumerAcknowledgementTestSupport.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/TcpServerConsumerAcknowledgementTestSupport.java index a83e9e5..2dad764 100644 --- a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/TcpServerConsumerAcknowledgementTestSupport.java +++ b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/TcpServerConsumerAcknowledgementTestSupport.java @@ -20,6 +20,7 @@ import java.util.concurrent.TimeUnit; import org.apache.camel.CamelContext; import org.apache.camel.EndpointInject; +import org.apache.camel.ExchangePattern; import org.apache.camel.LoggingLevel; import org.apache.camel.builder.NotifyBuilder; import org.apache.camel.builder.RouteBuilder; @@ -110,9 +111,9 @@ public abstract class TcpServerConsumerAcknowledgementTestSupport extends CamelT .log(LoggingLevel.INFO, routeId, "Test route complete") .to("mock://on-failure-only"); - fromF("mllp://%s:%d?bridgeErrorHandler=%b&autoAck=%b&connectTimeout=%d&receiveTimeout=%d", - mllpClient.getMllpHost(), mllpClient.getMllpPort(), isBridgeErrorHandler(), isAutoAck(), connectTimeout, - responseTimeout) + fromF("mllp://%s:%d?bridgeErrorHandler=%b&autoAck=%b&exchangePattern=%s&connectTimeout=%d&receiveTimeout=%d", + mllpClient.getMllpHost(), mllpClient.getMllpPort(), isBridgeErrorHandler(), isAutoAck(), + exchangePattern(), connectTimeout, responseTimeout) .routeId(routeId) .to(result); } @@ -123,6 +124,10 @@ public abstract class TcpServerConsumerAcknowledgementTestSupport extends CamelT protected abstract boolean isAutoAck(); + protected ExchangePattern exchangePattern() { + return ExchangePattern.InOut; + } + public void receiveSingleMessage() throws Exception { NotifyBuilder done = new NotifyBuilder(context).whenDone(1).create(); diff --git a/docs/components/modules/ROOT/pages/mllp-component.adoc b/docs/components/modules/ROOT/pages/mllp-component.adoc index 27b3c6b..d282b74 100644 --- a/docs/components/modules/ROOT/pages/mllp-component.adoc +++ b/docs/components/modules/ROOT/pages/mllp-component.adoc @@ -148,7 +148,9 @@ HL7 Acknowledgement (HL7 Application Acknowledgements only - AA, AE and AR), or the acknowledgement can be specified using the CamelMllpAcknowledgement exchange property. Additionally, the type of acknowledgement that will be generated can be controlled by setting -the CamelMllpAcknowledgementType exchange property. +the CamelMllpAcknowledgementType exchange property. The MLLP Consumer +can read messages without sending any HL7 Acknowledgement if the +automatic acknowledgement is disabled and exchange pattern is InOnly. == *Message Headers* @@ -206,7 +208,8 @@ The MLLP Producer supports sending MLLP-framed messages and receiving HL7 Acknowledgements. The MLLP Producer interrogates the HL7 Acknowledgments and raises exceptions if a negative acknowledgement is received. The received acknowledgement is interrogated and an exception -is raised in the event of a negative acknowledgement. +is raised in the event of a negative acknowledgement. The MLLP Producer +can ignore acknowledgements when configured with InOnly exchange pattern. == *Message Headers*