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^indiochin...@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^m82melc...@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 qu...@apache.org.