You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by qu...@apache.org on 2018/01/29 17:06:32 UTC

[camel] branch master updated: CAMEL-12200 - fix IndexOutOfBounds exception when generating acknowledgement

This is an automated email from the ASF dual-hosted git repository.

quinn 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 c7c9ea1  CAMEL-12200 - fix IndexOutOfBounds exception when generating acknowledgement
c7c9ea1 is described below

commit c7c9ea1d26011e24ccdd4aa84219625d41a56584
Author: Quinn Stevenson <qu...@apache.org>
AuthorDate: Mon Jan 29 09:20:38 2018 -0700

    CAMEL-12200 - fix IndexOutOfBounds exception when generating acknowledgement
---
 components/camel-mllp/src/data/mmodal_msg.txt      |  39 ------
 .../camel-mllp/src/data/prod-error-2017-11-07.txt  |  13 --
 .../apache/camel/component/mllp/MllpEndpoint.java  |  50 +------
 .../camel/component/mllp/internal/Hl7Util.java     | 130 ++++++++++++-----
 .../camel/component/mllp/MllpEndpointTest.java     |  47 -------
 .../camel/component/mllp/internal/Hl7UtilTest.java | 153 ++++++++++++++++++++-
 6 files changed, 248 insertions(+), 184 deletions(-)

diff --git a/components/camel-mllp/src/data/mmodal_msg.txt b/components/camel-mllp/src/data/mmodal_msg.txt
deleted file mode 100644
index 5599119..0000000
--- a/components/camel-mllp/src/data/mmodal_msg.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-MSH|^~\&|JCAPS|EPIC|JCAPS|CC|20171107103046|U0097718|ADT^A08|136545071|P|2.3
-EVN|A08|20171107103046||REG_UPDATE|U0097718^DAVIS^ALLYSON^MARIE^^^^^UCLA^^^^^SMH
-PID|1||2527477^^^MRN^MRN||TELLO^RUDY|LOPEZ|19340504|M||O|3481 ASHWOOD AVENUE^^LOS ANGELES^CA^90066^USA^P^^LOS ANGELE|LOS ANGELE|(310)390-2999^P^PH^^^310^3902999~^NET^Internet^indiochingon@gmail.com||ENGLISH|M|CATH|90053435300||||N|^^CUCAMONGA^CA^^|||US||USA||N
-PD1|||MC REGION 2^^41|017479^LEE^ALAN^C.^^^^^EPIC^^^^PROVID
-NK1|1|TELLO^ALICIA|SPO|3481 ASHWOOD AVE              ^^LOS ANGELES           ^CA^90066^USA|(310)390-2999^^PH^^^310^3902999|(310)384-7677^^PH^^^310^3847677|Emergency Contact 1
-NK1|2|||^^^^^USA|||Employer|||RETIRED|||NA||||||||||||||||||||1015|Retired
-PV1|1|O|SM MPU^SM MPU^14^1000^R^^^^^^DEPID|EL|||027422^SASSI^KAREEM^H.^^^^^EPIC^^^^PROVID|027422^SASSI^KAREEM^H.^^^^^EPIC^^^^PROVID||MGI||||HOM|||027422^SASSI^KAREEM^H.^^^^^EPIC^^^^PROVID||90053435300|MC|||||||||||||||||||||ADM_CONF|||20171107091811|||76.25
-PV2||Periop||||||20171107100000|20171107|0|||||||||||n|N
-ZPV||||||||||||20171107091811
-OBX|1|NM|HT^HEIGHT||5' 8"|ft||||||||20171011
-OBX|2|NM|WT^WEIGHT||2544|oz||||||||20171011
-AL1|1|Drug Class|^NO KNOWN ENVIRONMENTAL ALLERGIES^^NOTCOMPUTRITION^NO KNOWN ENVIRONMENTAL ALLERGIES^EXTELG
-DG1|1|ABF|Z12.11^Encounter for screening for malignant neoplasm of colon^ABF|Encounter for screening for malignant neoplasm of colon||VISIT
-DG1|2|ABF|Z12.11^Encounter for screening for malignant neoplasm of colon^ABF|Encounter for screening for malignant neoplasm of colon||ADMDXCODED
-DG1|3||^Special screening for malignant neoplasms, colon|Special screening for malignant neoplasms, colon||ADMDXTEXT
-GT1|1|3002910|TELLO^RUDY||3481 ASHWOOD AVE^^LOS ANGELES^CA^90066^USA^^^LOS ANGELE|(310)390-7361^^^^^310^3907361||19340504|M|P/F|SLF|573-40-4249||||NA|UNK^^LOS ANGELES^CA^90066^USA|(310)390-7361^^^^^310^3907361||Retired|||||||||||||||||||||||||||||Retired 
-IN1|1|20110105^UH_QET_SR_REG1|2011|UCLA MED GRP / UNITEDHEALTHCARE SR|5767 W CENTURY BLVD, STE 400^^LOS ANGELES^CA^90045-5631|||HCFAV1QETREG1||||20130101|20131231|||TELLO^RUDY|Self|19340504|3481 ASHWOOD AVE^^LOS ANGELES^CA^90066-2201^USA^^^LOS ANGELE|Accept||2|||NO||||||||||46255|466333501||||||Retired|M|^^^^^USA|MC Ver||BOTH
-IN2||573-40-4249|||Payor|573404249A|||||||||||||||||||||||||||||||||||||||||||||||||||||||466333501||(310)390-7361^^^^^310^3907361|(000)000-0000^^^^^000^0000000
-IN1|2|20110561^UH_Q45_SR_REG1|2011|UCLA MED GRP / UNITEDHEALTHCARE SR|5767 W CENTURY BLVD, STE 400^^LOS ANGELES^CA^90045-5631|||HCFAV1Q45REG1||||20160101|20161231|||TELLO^RUDY|Self|19340504|3481 ASHWOOD AVENUE^^LOS ANGELES^CA^90066^USA^^^LOS ANGELE|Accept||3|||NO||||||||||1669222|466333501|||||||M|^^^^^USA|MC Ver||BOTH
-IN2||573-40-4249|||Payor|573404249A|||||||||||||||||||||||||||||||||||||||||||||||||||||||466333501||(310)390-2999^^^^^310^3902999
-IN1|3|10170001^MEDICARE PART A \T\ B|1017|MEDICARE|PO BOX 6775^^FARGO^ND^58108-6775|||||||20130405|20130405|||TELLO^RUDY|Self|19340504|3481 ASHWOOD AVENUE^^LOS ANGELES^CA^90066^USA^^^LOS ANGELE|||4|||YES|||||RTEBATCH^RTE^BATCH^JOB|||||191427|573404249A||||||UNKNOWN|M|^^^^^USA|Elapsed||BOTH
-IN2||573-40-4249|||Payor||||||||||||||||||||||||||||||||||||||||||||||||||||||||573404249A||(310)390-2999^^^^^310^3902999
-IN1|4|20110296^UH_QYE_SR_REG1|2011|UCLA MED GRP / UNITEDHEALTHCARE SR|5767 W CENTURY BLVD, STE 400^^LOS ANGELES^CA^90045-5631|||HCFAV1QYEREG1||||20140101|20141231|||TELLO^RUDY|Self|19340504|3481 ASHWOOD AVENUE^^LOS ANGELES^CA^90066^^^^LOS ANGELE|Accept||5|||NO||||||||||948672|466333501||||||UNKNOWN|M|^^^^^USA|MC Ver||BOTH
-IN2||000-00-0001|||Payor|573404249A|||||||||||||||||||||||||||||||||||||||||||||||||||||||466333501||(310)390-7361^^^^^310^3907361|||||||NA
-IN1|5|20110455^UH_QM3_SR_REG1|2011|UCLA MED GRP / UNITEDHEALTHCARE SR|5767 W CENTURY BLVD, STE 400^^LOS ANGELES^CA^90045-5631|||HCFAV1QM3REG1||||20150101|20151231|||TELLO^RUDY|Self|19340504|3481 ASHWOOD AVENUE^^LOS ANGELES^CA^90066^USA^^^LOS ANGELE|Accept||6|||NO||||||||||1156515|466333501|||||||M|^^^^^USA|MC Ver||BOTH
-IN2||573-40-4249|||Payor|xxx-xx-4249|||||||||||||||||||||||||||||||||||||||||||||||||||||||466333501||(310)390-2999^^^^^310^3902999
-IN1|6|20050134^SC_2017H5425064LA_SR_REG2|2005|UCLA MED GRP / SCAN SR|PO BOX 22698^^LONG BEACH^CA^90801-5616|||H5425M2017H5425064LAREG2||||20170101||5006582182||TELLO^RUDY|Self|19340504|3481 ASHWOOD AVE^^LOS ANGELES^CA^90066^^^^LOS ANGELE|Accept||7**1|||NO||||||||||2205205|40021593301|||||||M||MC Ver||BOTH
-IN2||573-40-4249|||Payor|573404249A|||||||||||||||||||||||||||||||||||||||||||||||||||||||40021593301||(310)390-2999^^^^^310^3902999
-IN1|7|20130006^MGNV UCLAMG SCAN SENIOR|2013|MGNV PAYOR|5767 W CENTURY BLVD, STE 400^^LOS ANGELES^CA^90045-5631|||||||20170405|20170405|||TELLO^RUDY|Self|19340504|3481 ASHWOOD AVENUE^^LOS ANGELES^CA^90066^USA^^^LOS ANGELE|||8|||NO||||||||||2347659|40021593301||||||Retired|M|^^^^^USA|New||BOTH
-IN2||573-40-4249|||Payor||||||||||||||||||||||||||||||||||||||||||||||||||||||||40021593301|||||||||NA
-IN1|8|20130010^MGNV UCLAMG UNITED HEALTHCARE SENIOR|2013|MGNV PAYOR|5767 W CENTURY BLVD, STE 400^^LOS ANGELES^CA^90045-5631|||HCFAV1||||20140101|20140101|||TELLO^RUDY|Self|19340504|3481 ASHWOOD AVE^^LOS ANGELES^CA^90066-2201^USA^^^LOS ANGELE|||9|||NO|||||U0058266^TERM-RIOS^MICHAEL^ARTHUR|||||640778|466333501||||||Retired|M|^^^^^USA|Elapsed||BOTH
-IN2||573-40-4249|||Payor||||||||||||||||||||||||||||||||||||||||||||||||||||||||466333501|||(000)000-0000^^^^^000^0000000
-IN1|9|20130010^MGNV UCLAMG UNITED HEALTHCARE SENIOR|2013|MGNV PAYOR|5767 W CENTURY BLVD, STE 400^^LOS ANGELES^CA^90045-5631|||HCFAV1||||20150101|20150101|||TELLO^RUDY|Self|19340504|3481 ASHWOOD AVENUE^^LOS ANGELES^CA^90066^USA^^^LOS ANGELE|||10|||NO|||||U0032994^ANDRADE^DAVID^A|||||1538176|466333501||||||Retired|M|^^^^^USA|Elapsed||BOTH
-IN2||573-40-4249|||Payor||||||||||||||||||||||||||||||||||||||||||||||||||||||||466333501|||(000)000-0000^^^^^000^0000000
-IN1|10|20130010^MGNV UCLAMG UNITED HEALTHCARE SENIOR|2013|MGNV PAYOR|5767 W CENTURY BLVD, STE 400^^LOS ANGELES^CA^90045-5631|||HCFAV1||||20160101|20160101|||TELLO^RUDY|Self|19340504|3481 ASHWOOD AVENUE^^LOS ANGELES^CA^90066^USA^^^LOS ANGELE|||11|||NO|||||U0032994^ANDRADE^DAVID^A|||||2125557|466333501||||||Retired|M|^^^^^USA|Elapsed||BOTH
-IN2||573-40-4249|||Payor||||||||||||||||||||||||||||||||||||||||||||||||||||||||466333501|||||||||NA
-IN1|11|20130023^MGNV SMBP SCAN SENIOR|2013|MGNV PAYOR|5767 W CENTURY BLVD, STE 400^^LOS ANGELES^CA^90045-5631|||HCFAV1||||20180101|20180101|||TELLO^RUDY|Self|19340504|3481 ASHWOOD AVENUE^^LOS ANGELES^CA^90066^USA^^^LOS ANGELE|||12|||NO|||||U0032994^ANDRADE^DAVID^A|||||2238968|40021593301||||||Retired|M|^^^^^USA|Elapsed||BOTH
-IN2||573-40-4249|||Payor||||||||||||||||||||||||||||||||||||||||||||||||||||||||40021593301|||||||||NA
-
diff --git a/components/camel-mllp/src/data/prod-error-2017-11-07.txt b/components/camel-mllp/src/data/prod-error-2017-11-07.txt
deleted file mode 100644
index 7209d69..0000000
--- a/components/camel-mllp/src/data/prod-error-2017-11-07.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-2017-11-07 00:25:43,676 | WARN | MllpTcpServerConsumer$ConsumerAcceptSocketThread[mllp://27000] - 0.0.0.0/0.0.0.0:27000 | edu.ucla.mednet.iss.it.camel.component.mllp.MllpTcpServerConsumer | {{bundle.id,130}{bundle.name,org.apache.camel.camel-core}{bundle.version,2.17.0.redhat-630283}} | Ignoring exception encountered while attempting to read the initial payload - ConsumerClientSocketThread will not be started
-edu.ucla.mednet.iss.it.camel.component.mllp.internal.MllpSocketTimeoutException: Timeout reading the end of the MLLP payload
-        {hl7Message= MSH|^~\&|ADT|EPIC|JCAPS|CC|20171107002534|U0096036|ADT^A08|136521583|P|2.3<CR>EVN|A08|20171107002534||REG_UPDATE|U0096036^MCCARRON^LAURA^CHRISTINE^^^^^UCLA^^^^^SMH<CR>PID|1|0922689^^^MRN^MRN|0922689^^^MRN^MRN||MELCHOR^MARIA^DEL RE|GONZALEZ|19570704|F|GONZALEZ^MARIA^DEL RE~MELCHOR^MARIA~MELCHOR^MARIA^DEL RE|O|7900 READING AVENUE^^LOS ANGELES^CA^90045^USA^P^^LOS ANGELE|LOS ANGELE|(310)743-7722^P^PH^^^310^7437722~^NET^Internet^m82melchor@yahoo.com~(310)743-7722^P^CP^^^3 [...]
-        at edu.ucla.mednet.iss.it.camel.component.mllp.internal.MllpSocketReader.readEnvelopedPayload(MllpSocketReader.java:192)[112:edu.ucla.mednet.iss.it.camel-camel.mllp.ucla:0.1.8]
-        at edu.ucla.mednet.iss.it.camel.component.mllp.MllpTcpServerConsumer$ConsumerAcceptSocketThread.run(MllpTcpServerConsumer.java:324)[112:edu.ucla.mednet.iss.it.camel-camel.mllp.ucla:0.1.8]
-Caused by: java.net.SocketTimeoutException: Read timed out
-        at java.net.SocketInputStream.socketRead0(Native Method)[:1.8.0_151]
-        at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)[:1.8.0_151]
-        at java.net.SocketInputStream.read(SocketInputStream.java:171)[:1.8.0_151]
-        at java.net.SocketInputStream.read(SocketInputStream.java:141)[:1.8.0_151]
-        at java.net.SocketInputStream.read(SocketInputStream.java:127)[:1.8.0_151]
-        at edu.ucla.mednet.iss.it.camel.component.mllp.internal.MllpSocketReader.readEnvelopedPayload(MllpSocketReader.java:177)[112:edu.ucla.mednet.iss.it.camel-camel.mllp.ucla:0.1.8]
-        ... 1 more
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpEndpoint.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpEndpoint.java
index c7d9d6e..5c0338a 100644
--- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpEndpoint.java
+++ b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpEndpoint.java
@@ -33,6 +33,7 @@ import org.apache.camel.Processor;
 import org.apache.camel.Producer;
 import org.apache.camel.api.management.ManagedAttribute;
 import org.apache.camel.api.management.ManagedResource;
+import org.apache.camel.component.mllp.internal.Hl7Util;
 import org.apache.camel.impl.DefaultEndpoint;
 import org.apache.camel.spi.Metadata;
 import org.apache.camel.spi.UriEndpoint;
@@ -233,7 +234,7 @@ public class MllpEndpoint extends DefaultEndpoint {
                 LOG.warn("Unsupported Character Set {} specified for MSH-18 - using default character set {}", msh18, MllpProtocolConstants.DEFAULT_CHARSET);
             }
         } else {
-            String foundMsh18 = findMsh18(hl7Bytes);
+            String foundMsh18 = Hl7Util.findMsh18(hl7Bytes);
             if (foundMsh18 != null && !foundMsh18.isEmpty()) {
                 if (MllpProtocolConstants.MSH18_VALUES.containsKey(foundMsh18)) {
                     answer = MllpProtocolConstants.MSH18_VALUES.get(foundMsh18);
@@ -262,54 +263,7 @@ public class MllpEndpoint extends DefaultEndpoint {
         return new String(hl7Bytes, charset);
     }
 
-    // TODO:  Move this to HL7Util
-    public String findMsh18(byte[] hl7Message) {
-        if (hl7Message == null || hl7Message.length == 0) {
-            return null;
-        }
 
-        final byte fieldSeparator = hl7Message[3];
-        int endOfMSH = -1;
-        List<Integer> fieldSeparatorIndexes = new ArrayList<>(10);  // We should have at least 10 fields
-
-        for (int i = 0; i < hl7Message.length; ++i) {
-            if (fieldSeparator == hl7Message[i]) {
-                fieldSeparatorIndexes.add(i);
-            } else if (MllpProtocolConstants.SEGMENT_DELIMITER == hl7Message[i]) {
-                // If the MSH Segment doesn't have a trailing field separator, add one so the field can be extracted into a string
-                if (fieldSeparator != hl7Message[i - 1]) {
-                    fieldSeparatorIndexes.add(i);
-                }
-                endOfMSH = i;
-                break;
-            }
-        }
-
-        if (fieldSeparatorIndexes.size() >= 18) {
-            int startingFieldSeparatorIndex = fieldSeparatorIndexes.get(17);
-            int length = 0;
-
-            if (fieldSeparatorIndexes.size() >= 19) {
-                length = fieldSeparatorIndexes.get(18) - startingFieldSeparatorIndex - 1;
-            } else {
-                length = endOfMSH - startingFieldSeparatorIndex - 1;
-            }
-
-            if (length < 0) {
-                return null;
-            } else if (length == 0) {
-                return "";
-            }
-
-            String msh18value = new String(hl7Message, startingFieldSeparatorIndex + 1,
-                length,
-                StandardCharsets.US_ASCII);
-
-            return msh18value;
-        }
-
-        return null;
-    }
 
     // Pass-through configuration methods
     public void setBacklog(Integer backlog) {
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/internal/Hl7Util.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/internal/Hl7Util.java
index 40390f1..67f86cb 100644
--- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/internal/Hl7Util.java
+++ b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/internal/Hl7Util.java
@@ -17,7 +17,9 @@
 
 package org.apache.camel.component.mllp.internal;
 
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.LinkedList;
 import java.util.List;
 
 import org.apache.camel.component.mllp.MllpComponent;
@@ -82,10 +84,9 @@ public final class Hl7Util {
 
         int validationLength = Math.min(length, hl7Bytes.length);
 
-        if (hl7Bytes[validationLength - 2] != MllpProtocolConstants.SEGMENT_DELIMITER || hl7Bytes[validationLength - 1] != MllpProtocolConstants.MESSAGE_TERMINATOR) {
-            String format = "The HL7 payload terminating bytes [%#x, %#x] are incorrect - expected [%#x, %#x]  {ASCII [<CR>, <LF>]}";
-            return String.format(format, hl7Bytes[validationLength - 2], hl7Bytes[validationLength - 1],
-                (byte) MllpProtocolConstants.SEGMENT_DELIMITER, (byte) MllpProtocolConstants.MESSAGE_TERMINATOR);
+        if (hl7Bytes[validationLength - 1] != MllpProtocolConstants.SEGMENT_DELIMITER  && hl7Bytes[validationLength - 1] != MllpProtocolConstants.MESSAGE_TERMINATOR) {
+            String format = "The HL7 payload terminating byte [%#x] is incorrect - expected [%#x]  {ASCII [<CR>]}";
+            return String.format(format, hl7Bytes[validationLength - 2], (byte) MllpProtocolConstants.SEGMENT_DELIMITER);
         }
 
         for (int i = 0; i < validationLength; ++i) {
@@ -102,34 +103,78 @@ public final class Hl7Util {
         return null;
     }
 
-    public static void generateAcknowledgementPayload(MllpSocketBuffer mllpSocketBuffer, byte[] hl7MessageBytes, String acknowledgementCode) throws Hl7AcknowledgementGenerationException {
-        if (hl7MessageBytes == null) {
-            throw new Hl7AcknowledgementGenerationException("Null HL7 message received for parsing operation");
+    /**
+     * Find the field separator indices in the Segment.
+     *
+     * NOTE:  The last element of the list will be the index of the end of the segment.
+     *
+     * @param hl7MessageBytes the HL7 binary message
+     * @param startingIndex index of the beginning of the HL7 Segment
+     *
+     * @return List of the field separator indices, which may be empty.
+     */
+    public static List<Integer> findFieldSeparatorIndicesInSegment(byte[] hl7MessageBytes, int startingIndex) {
+        List<Integer> fieldSeparatorIndices = new LinkedList<>();
+
+        if (hl7MessageBytes != null && hl7MessageBytes.length > startingIndex && hl7MessageBytes.length > 3) {
+            final byte fieldSeparator = hl7MessageBytes[3];
+
+            for (int i = startingIndex; i < hl7MessageBytes.length; ++i) {
+                if (fieldSeparator == hl7MessageBytes[i]) {
+                    fieldSeparatorIndices.add(i);
+                } else if (MllpProtocolConstants.SEGMENT_DELIMITER == hl7MessageBytes[i]) {
+                    fieldSeparatorIndices.add(i);
+                    break;
+                }
+            }
         }
 
-        final byte fieldSeparator = hl7MessageBytes[3];
-        final byte componentSeparator = hl7MessageBytes[4];
+        return fieldSeparatorIndices;
+    }
+
+    /**
+     * Find the String value of MSH-19 (Character set).
+     *
+     * @param hl7Message the HL7 binary data to search
+     *
+     * @return the String value of MSH-19, or an empty String if not found.
+     */
+    public static String findMsh18(byte[] hl7Message) {
+        String answer = "";
 
-        List<Integer> fieldSeparatorIndexes = new ArrayList<>(10);  // We need at least 10 fields to create the acknowledgment
+        if (hl7Message != null && hl7Message.length > 0) {
 
-        // Find the end of the MSH and indexes of the fields in the MSH
-        int endOfMSH = -1;
-        for (int i = 0; i < hl7MessageBytes.length; ++i) {
-            if (fieldSeparator == hl7MessageBytes[i]) {
-                fieldSeparatorIndexes.add(i);
-            } else if (MllpProtocolConstants.SEGMENT_DELIMITER == hl7MessageBytes[i]) {
-                endOfMSH = i;
-                break;
+            List<Integer> fieldSeparatorIndexes = findFieldSeparatorIndicesInSegment(hl7Message, 0);
+
+            if (fieldSeparatorIndexes.size() > 18) {
+                int startOfMsh19 = fieldSeparatorIndexes.get(17) + 1;
+                int length = fieldSeparatorIndexes.get(18) - fieldSeparatorIndexes.get(17) - 1;
+
+                if (length > 0) {
+                    answer = new String(hl7Message, startOfMsh19, length, StandardCharsets.US_ASCII);
+                }
             }
         }
 
-        if (-1 == endOfMSH) {
-            throw new Hl7AcknowledgementGenerationException("Failed to find the end of the  MSH Segment while attempting to generate response", hl7MessageBytes);
+        return answer;
+    }
+
+
+    public static void generateAcknowledgementPayload(MllpSocketBuffer mllpSocketBuffer, byte[] hl7MessageBytes, String acknowledgementCode) throws Hl7AcknowledgementGenerationException {
+        if (hl7MessageBytes == null) {
+            throw new Hl7AcknowledgementGenerationException("Null HL7 message received for parsing operation");
         }
 
-        if (8 > fieldSeparatorIndexes.size()) {
-            throw new Hl7AcknowledgementGenerationException("Insufficient number of fields in after MSH-2 in MSH to generate a response - 8 are required but "
-                + fieldSeparatorIndexes.size() + " " + "were found", hl7MessageBytes);
+        List<Integer> fieldSeparatorIndexes = findFieldSeparatorIndicesInSegment(hl7MessageBytes, 0);
+
+        if (fieldSeparatorIndexes.isEmpty()) {
+            throw new Hl7AcknowledgementGenerationException("Failed to find the end of the MSH Segment while attempting to generate response", hl7MessageBytes);
+        }
+
+        if (fieldSeparatorIndexes.size() < 8) {
+            String exceptionMessage = String.format("Insufficient number of fields in MSH-2 in MSH to generate a response - 10 are required but %d were found", fieldSeparatorIndexes.size() - 1);
+
+            throw new Hl7AcknowledgementGenerationException(exceptionMessage, hl7MessageBytes);
         }
 
         // Start building the MLLP Envelope
@@ -137,36 +182,44 @@ public final class Hl7Util {
 
         // Build the MSH Segment
         mllpSocketBuffer.write(hl7MessageBytes, 0, fieldSeparatorIndexes.get(1)); // through MSH-2 (without trailing field separator)
-        mllpSocketBuffer.write(hl7MessageBytes, fieldSeparatorIndexes.get(3), fieldSeparatorIndexes.get(4) - fieldSeparatorIndexes.get(3)); // MSH-5
-        mllpSocketBuffer.write(hl7MessageBytes, fieldSeparatorIndexes.get(4), fieldSeparatorIndexes.get(5) - fieldSeparatorIndexes.get(4)); // MSH-6
-        mllpSocketBuffer.write(hl7MessageBytes, fieldSeparatorIndexes.get(1), fieldSeparatorIndexes.get(2) - fieldSeparatorIndexes.get(1)); // MSH-3
-        mllpSocketBuffer.write(hl7MessageBytes, fieldSeparatorIndexes.get(2), fieldSeparatorIndexes.get(3) - fieldSeparatorIndexes.get(2)); // MSH-4
-        mllpSocketBuffer.write(hl7MessageBytes, fieldSeparatorIndexes.get(5), fieldSeparatorIndexes.get(7) - fieldSeparatorIndexes.get(5)); // MSH-7 and MSH-8
+        writeFieldToBuffer(3, mllpSocketBuffer, hl7MessageBytes, fieldSeparatorIndexes); // MSH-5
+        writeFieldToBuffer(4, mllpSocketBuffer, hl7MessageBytes, fieldSeparatorIndexes); // MSH-6
+        writeFieldToBuffer(1, mllpSocketBuffer, hl7MessageBytes, fieldSeparatorIndexes); // MSH-3
+        writeFieldToBuffer(2, mllpSocketBuffer, hl7MessageBytes, fieldSeparatorIndexes); // MSH-4
+        writeFieldToBuffer(5, mllpSocketBuffer, hl7MessageBytes, fieldSeparatorIndexes); // MSH-7
+        writeFieldToBuffer(6, mllpSocketBuffer, hl7MessageBytes, fieldSeparatorIndexes); // MSH-8
+
+        final byte fieldSeparator = hl7MessageBytes[3];
+
         // Need to generate the correct MSH-9
         mllpSocketBuffer.write(fieldSeparator);
-        mllpSocketBuffer.write("ACK".getBytes(), 0, 3); // MSH-9.1
+        mllpSocketBuffer.write("ACK".getBytes()); // MSH-9.1
         int msh92start = -1;
         for (int j = fieldSeparatorIndexes.get(7) + 1; j < fieldSeparatorIndexes.get(8); ++j) {
+            final byte componentSeparator = hl7MessageBytes[4];
             if (componentSeparator == hl7MessageBytes[j]) {
                 msh92start = j;
                 break;
             }
         }
 
+        // MSH-9.2
         if (-1 == msh92start) {
             LOG.warn("Didn't find component separator for MSH-9.2 - sending ACK in MSH-9");
         } else {
-            mllpSocketBuffer.write(hl7MessageBytes, msh92start, fieldSeparatorIndexes.get(8) - msh92start); // MSH-9.2
+            mllpSocketBuffer.write(hl7MessageBytes, msh92start, fieldSeparatorIndexes.get(8) - msh92start);
         }
 
-        mllpSocketBuffer.write(hl7MessageBytes, fieldSeparatorIndexes.get(8), endOfMSH - fieldSeparatorIndexes.get(8)); // MSH-10 through the end of the MSH
+        // MSH-10 through the end of the MSH
+        mllpSocketBuffer.write(hl7MessageBytes, fieldSeparatorIndexes.get(8), fieldSeparatorIndexes.get(fieldSeparatorIndexes.size() - 1) - fieldSeparatorIndexes.get(8));
+
         mllpSocketBuffer.write(MllpProtocolConstants.SEGMENT_DELIMITER);
 
         // Build the MSA Segment
         mllpSocketBuffer.write("MSA".getBytes(), 0, 3);
         mllpSocketBuffer.write(fieldSeparator);
         mllpSocketBuffer.write(acknowledgementCode.getBytes(), 0, 2);
-        mllpSocketBuffer.write(hl7MessageBytes, fieldSeparatorIndexes.get(8), fieldSeparatorIndexes.get(9) - fieldSeparatorIndexes.get(8)); // MSH-10 end
+        writeFieldToBuffer(8, mllpSocketBuffer, hl7MessageBytes, fieldSeparatorIndexes); // MSH-10
         mllpSocketBuffer.write(MllpProtocolConstants.SEGMENT_DELIMITER);
 
         // Close the MLLP Envelope
@@ -333,4 +386,17 @@ public final class Hl7Util {
             return String.valueOf(c);
         }
     }
+
+    /**
+     * Copy a field from the HL7 Message Bytes to the supplied MllpSocketBuffer.
+     *
+     * NOTE:  Internal function - no error checking
+     *
+     * @param mllpSocketBuffer the destination for the field
+     * @param hl7MessageBytes the HL7 message bytes
+     * @param fieldSeparatorIndexes the list of the indices of the field separators
+     */
+    private static void writeFieldToBuffer(int fieldNumber, MllpSocketBuffer mllpSocketBuffer, byte[] hl7MessageBytes, List<Integer> fieldSeparatorIndexes) {
+        mllpSocketBuffer.write(hl7MessageBytes, fieldSeparatorIndexes.get(fieldNumber), fieldSeparatorIndexes.get(fieldNumber + 1) - fieldSeparatorIndexes.get(fieldNumber));
+    }
 }
diff --git a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpEndpointTest.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpEndpointTest.java
index 7818efb..304726b 100644
--- a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpEndpointTest.java
+++ b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpEndpointTest.java
@@ -145,51 +145,4 @@ public class MllpEndpointTest {
         assertEquals(testMessage, instance.createNewString(testMessage.getBytes(StandardCharsets.ISO_8859_1), null));
     }
 
-    /**
-     * Description of test.
-     *
-     * @throws Exception in the event of a test error.
-     */
-    @Test
-    public void testFindMsh18WhenExistsWithoutTrailingPipe() throws Exception {
-        final String testMessage = MSH_SEGMENT + "|||||||8859/1" + '\r' + REMAINING_SEGMENTS;
-
-        assertEquals("8859/1", instance.findMsh18(testMessage.getBytes()));
-    }
-
-    /**
-     * Description of test.
-     *
-     * @throws Exception in the event of a test error.
-     */
-    @Test
-    public void testFindMsh18WhenExistsWithTrailingPipe() throws Exception {
-        final String testMessage = MSH_SEGMENT + "|||||||8859/1|" + '\r' + REMAINING_SEGMENTS;
-
-        assertEquals("8859/1", instance.findMsh18(testMessage.getBytes()));
-    }
-
-    /**
-     * Description of test.
-     *
-     * @throws Exception in the event of a test error.
-     */
-    @Test
-    public void testFindMsh18WhenMissingWithoutTrailingPipe() throws Exception {
-        final String testMessage = MSH_SEGMENT + "|||||||" + '\r' + REMAINING_SEGMENTS;
-
-        assertEquals("", instance.findMsh18(testMessage.getBytes()));
-    }
-
-    /**
-     * Description of test.
-     *
-     * @throws Exception in the event of a test error.
-     */
-    @Test
-    public void testFindMsh18WhenMissingWithTrailingPipe() throws Exception {
-        final String testMessage = MSH_SEGMENT + "||||||||" + '\r' + REMAINING_SEGMENTS;
-
-        assertEquals("", instance.findMsh18(testMessage.getBytes()));
-    }
 }
\ No newline at end of file
diff --git a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/internal/Hl7UtilTest.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/internal/Hl7UtilTest.java
index bdecd21..dcd7801 100644
--- a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/internal/Hl7UtilTest.java
+++ b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/internal/Hl7UtilTest.java
@@ -21,6 +21,8 @@ import java.io.ByteArrayOutputStream;
 
 import org.apache.camel.component.mllp.MllpProtocolConstants;
 
+import org.apache.camel.processor.mllp.Hl7AcknowledgementGenerationException;
+import org.apache.camel.test.stub.camel.MllpEndpointStub;
 import org.junit.Test;
 
 import static org.junit.Assert.assertEquals;
@@ -44,8 +46,13 @@ public class Hl7UtilTest {
             + "DG1|1|DX|784.0^Headache^DX|Headache||VISIT" + '\r'
             + "GT1|1|1000235129|MDCLS9^MC9^^||111 HOVER STREET^^LOS ANGELES^CA^90032^USA^^^LOS ANGELE|(310)725-6952^^^^^310^7256952||19700109|F|P/F|SLF|"
             + "565-33-2222|||||^^^^^USA|||UNKNOWN|||||||||||||||||||||||||||||" + '\r'
-            + "UB2||||||||" + '\r'
-            + '\n';
+            + "UB2||||||||" + '\r';
+
+    static final String EXPTECTED_ACKNOWLEDGEMENT_PAYLOAD =
+        MllpProtocolConstants.START_OF_BLOCK
+            + "MSH|^~\\&|JCAPS|CC|ADT|EPIC|20161206193919|RISTECH|ACK^A08|00001|D|2.3^^|||||||\r"
+            + "MSA|AA|00001\r"
+            + MllpProtocolConstants.END_OF_BLOCK + MllpProtocolConstants.END_OF_DATA;
 
     static final String EXPECTED_MESSAGE =
         "MSH|^~\\&|ADT|EPIC|JCAPS|CC|20161206193919|RISTECH|ADT^A08|00001|D|2.3^^|||||||" + "<CR>"
@@ -62,8 +69,21 @@ public class Hl7UtilTest {
             + "DG1|1|DX|784.0^Headache^DX|Headache||VISIT" + "<CR>"
             + "GT1|1|1000235129|MDCLS9^MC9^^||111 HOVER STREET^^LOS ANGELES^CA^90032^USA^^^LOS ANGELE|(310)725-6952^^^^^310^7256952||19700109|F|P/F|SLF|565-33-2222|||||^^^^^USA|||UNKNOWN"
             +     "|||||||||||||||||||||||||||||" + "<CR>"
-            + "UB2||||||||" + "<CR>"
-            + "<LF>";
+            + "UB2||||||||" + "<CR>";
+    // @formatter:on
+
+    static final String MSH_SEGMENT = "MSH|^~\\&|0|90100053675|JCAPS|CC|20131125122938|RISMD|ORM|28785|D|2.3";
+
+    // @formatter:off
+    static final String REMAINING_SEGMENTS =
+        "PID|1||4507626^^^MRN^MRN||RAD VALIDATE^ROBERT||19650916|M||U|1818 UNIVERSITY AVE^^MADISON^WI^53703^USA^^^||(608)251-9999|||M|||579-85-3510||| " + '\r'
+            + "PV1||OUTPATIENT|NMPCT^^^WWNMD^^^^^^^DEPID||||011463^ZARAGOZA^EDWARD^J.^^^^^EPIC^^^^PROVID|011463^ZARAGOZA^EDWARD^J.^^^^^EPIC^^^^PROVID"
+            +     "|||||||||||90100053686|SELF||||||||||||||||||||||||201311251218|||||||V" + '\r'
+            + "ORC|RE|9007395^EPC|9007395^EPC||Final||^^^201311251221^201311251222^R||201311251229|RISMD^RADIOLOGY^RADIOLOGIST^^|||SMO PET^^^7044^^^^^SMO PET CT||||||||||||||||I" + '\r'
+            + "OBR|1|9007395^EPC|9007395^EPC|IMG7118^PET CT LIMITED CHEST W CONTRAST^IMGPROC^^PET CT CHEST||20131125|||||Ancillary Pe|||||||NMPCT|MP2 NM INJ01^MP2 NM INJECTION ROOM 01^PROVID"
+            +     "|||201311251229||NM|Final||^^^201311251221^201311251222^R||||^test|E200003^RADIOLOGY^RESIDENT^^^^^^EPIC^^^^PROVID"
+            +     "|812644^RADIOLOGY^GENERIC^ATTENDING 1^^^^^EPIC^^^^PROVID~000043^RADIOLOGY^RADIOLOGISTTWO^^^^^^EPIC^^^^PROVID|U0058489^SWAIN^CYNTHIA^LEE^||201311251245" + '\r'
+            + "OBX|1|ST|&GDT|1|[11/25/2013 12:28:14 PM - PHYS, FIFTYFOUR]50||||||Final||||" + '\r';
     // @formatter:on
 
     static final byte[] TEST_MESSAGE_BYTES = TEST_MESSAGE.getBytes();
@@ -87,7 +107,7 @@ public class Hl7UtilTest {
         byte[] payload = TEST_MESSAGE.getBytes();
         String message = Hl7Util.generateInvalidPayloadExceptionMessage(payload, 10);
 
-        assertEquals("The HL7 payload terminating bytes [0x7c, 0x41] are incorrect - expected [0xd, 0xa]  {ASCII [<CR>, <LF>]}", message);
+        assertEquals("The HL7 payload terminating byte [0x7c] is incorrect - expected [0xd]  {ASCII [<CR>]}", message);
     }
 
     @Test
@@ -155,6 +175,81 @@ public class Hl7UtilTest {
      * @throws Exception in the event of a test error.
      */
     @Test
+    public void testGenerateAcknowledgementPayload() throws Exception {
+        MllpSocketBuffer mllpSocketBuffer = new MllpSocketBuffer(new MllpEndpointStub());
+        Hl7Util.generateAcknowledgementPayload(mllpSocketBuffer, TEST_MESSAGE.getBytes(), "AA");
+
+        assertEquals(EXPTECTED_ACKNOWLEDGEMENT_PAYLOAD, mllpSocketBuffer.toString());
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test(expected = Hl7AcknowledgementGenerationException.class)
+    public void testGenerateAcknowledgementPayloadFromNullMessage() throws Exception {
+        MllpSocketBuffer mllpSocketBuffer = new MllpSocketBuffer(new MllpEndpointStub());
+        Hl7Util.generateAcknowledgementPayload(mllpSocketBuffer, null, "AA");
+
+        assertEquals(EXPTECTED_ACKNOWLEDGEMENT_PAYLOAD, mllpSocketBuffer.toString());
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test(expected = Hl7AcknowledgementGenerationException.class)
+    public void testGenerateAcknowledgementPayloadFromEmptyMessage() throws Exception {
+        MllpSocketBuffer mllpSocketBuffer = new MllpSocketBuffer(new MllpEndpointStub());
+        Hl7Util.generateAcknowledgementPayload(mllpSocketBuffer, new byte[0], "AA");
+
+        assertEquals(EXPTECTED_ACKNOWLEDGEMENT_PAYLOAD, mllpSocketBuffer.toString());
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testGenerateAcknowledgementPayloadWithoutEnoughFields() throws Exception {
+        MllpSocketBuffer mllpSocketBuffer = new MllpSocketBuffer(new MllpEndpointStub());
+        Hl7Util.generateAcknowledgementPayload(mllpSocketBuffer, TEST_MESSAGE.replaceFirst("|RISTECH|ADT^A08|00001|D|2.3^^|||||||", "").getBytes(), "AA");
+
+        assertEquals(EXPTECTED_ACKNOWLEDGEMENT_PAYLOAD, mllpSocketBuffer.toString());
+    }
+
+    /**
+     * If the MSH isn't terminated correctly, we'll get Junk for the acknowledgement.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testGenerateAcknowledgementPayloadWithoutEndOfSegment() throws Exception {
+        String junkMessage = "MSH|^~\\&|ADT|EPIC|JCAPS|CC|20161206193919|RISTECH|ADT^A08|00001|D|2.3^^|||||||"
+            + "EVN|A08|20150107161440||REG_UPDATE_SEND_VISIT_MESSAGES_ON_PATIENT_CHANGES|RISTECH^RADIOLOGY^TECHNOLOGIST^^^^^^UCLA^^^^^RRMC||";
+
+        String junkAcknowledgement =
+            MllpProtocolConstants.START_OF_BLOCK
+                + "MSH|^~\\&|JCAPS|CC|ADT|EPIC|20161206193919|RISTECH|ACK^A08|00001|D|2.3^^|||||||"
+                + "EVN|A08|20150107161440||REG_UPDATE_SEND_VISIT_MESSAGES_ON_PATIENT_CHANGES|RISTECH^RADIOLOGY^TECHNOLOGIST^^^^^^UCLA^^^^^RRMC|\r"
+                + "MSA|AA|00001\r"
+                + MllpProtocolConstants.END_OF_BLOCK + MllpProtocolConstants.END_OF_DATA;
+
+        MllpSocketBuffer mllpSocketBuffer = new MllpSocketBuffer(new MllpEndpointStub());
+        Hl7Util.generateAcknowledgementPayload(mllpSocketBuffer, junkMessage.getBytes(), "AA");
+
+        assertEquals(junkAcknowledgement, mllpSocketBuffer.toString());
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
     public void testConvertStringToPrintFriendlyString() throws Exception {
         assertEquals(Hl7Util.NULL_REPLACEMENT_VALUE, Hl7Util.convertToPrintFriendlyString((String) null));
         assertEquals(Hl7Util.EMPTY_REPLACEMENT_VALUE, Hl7Util.convertToPrintFriendlyString(""));
@@ -511,4 +606,52 @@ public class Hl7UtilTest {
         assertEquals(Hl7Util.MESSAGE_TERMINATOR_REPLACEMENT_VALUE, Hl7Util.getCharacterAsPrintFriendlyString(MllpProtocolConstants.MESSAGE_TERMINATOR));
         assertEquals(Hl7Util.TAB_REPLACEMENT_VALUE, Hl7Util.getCharacterAsPrintFriendlyString('\t'));
     }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testFindMsh18WhenExistsWithoutTrailingPipe() throws Exception {
+        final String testMessage = MSH_SEGMENT + "|||||||8859/1" + '\r' + REMAINING_SEGMENTS;
+
+        assertEquals("8859/1", Hl7Util.findMsh18(testMessage.getBytes()));
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testFindMsh18WhenExistsWithTrailingPipe() throws Exception {
+        final String testMessage = MSH_SEGMENT + "|||||||8859/1|" + '\r' + REMAINING_SEGMENTS;
+
+        assertEquals("8859/1", Hl7Util.findMsh18(testMessage.getBytes()));
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testFindMsh18WhenMissingWithoutTrailingPipe() throws Exception {
+        final String testMessage = MSH_SEGMENT + "|||||||" + '\r' + REMAINING_SEGMENTS;
+
+        assertEquals("", Hl7Util.findMsh18(testMessage.getBytes()));
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testFindMsh18WhenMissingWithTrailingPipe() throws Exception {
+        final String testMessage = MSH_SEGMENT + "||||||||" + '\r' + REMAINING_SEGMENTS;
+
+        assertEquals("", Hl7Util.findMsh18(testMessage.getBytes()));
+    }
 }
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
quinn@apache.org.