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/12 17:51:18 UTC

[camel] branch master updated: CAMEL-12106 : Merge of fork

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 b51c59b  CAMEL-12106 : Merge of fork
b51c59b is described below

commit b51c59b3b24b7d61d3e600c3fe13f35232525b02
Author: Quinn Stevenson <qu...@apache.org>
AuthorDate: Fri Jan 12 10:50:03 2018 -0700

    CAMEL-12106 : Merge of fork
---
 components/camel-mllp/pom.xml                      |  15 +
 components/camel-mllp/src/data/mmodal_msg.txt      |  39 +
 .../camel-mllp/src/data/prod-error-2017-11-07.txt  |  13 +
 .../mllp/MllpAcknowledgementDeliveryException.java |   2 +-
 .../mllp/MllpAcknowledgementException.java         |   7 +-
 ...va => MllpAcknowledgementReceiveException.java} |  26 +-
 .../mllp/MllpAcknowledgementTimeoutException.java  |  11 +-
 ...lpApplicationErrorAcknowledgementException.java |   2 +-
 ...pApplicationRejectAcknowledgementException.java |   2 +-
 .../MllpCommitErrorAcknowledgementException.java   |   2 +-
 .../MllpCommitRejectAcknowledgementException.java  |   2 +-
 .../apache/camel/component/mllp/MllpComponent.java | 117 +--
 .../camel/component/mllp/MllpConfiguration.java    | 603 +++++++++++++++
 .../apache/camel/component/mllp/MllpConstants.java |   8 +-
 .../apache/camel/component/mllp/MllpEndpoint.java  | 591 ++++++++-------
 .../apache/camel/component/mllp/MllpException.java | 135 ++--
 .../camel/component/mllp/MllpFrameException.java   |  43 --
 .../mllp/MllpInvalidAcknowledgementException.java  |   2 +-
 .../mllp/MllpInvalidMessageException.java          |   2 +-
 .../mllp/MllpNegativeAcknowledgementException.java |   2 +-
 .../component/mllp/MllpProtocolConstants.java      |  73 ++
 .../camel/component/mllp/MllpReceiveException.java |   2 +-
 ...sageException.java => MllpSocketException.java} |  15 +-
 .../component/mllp/MllpTcpClientProducer.java      | 451 ++++++++----
 .../component/mllp/MllpTcpServerConsumer.java      | 807 +++------------------
 .../camel/component/mllp/MllpTimeoutException.java |  61 --
 .../camel/component/mllp/MllpWriteException.java   |   1 +
 .../apache/camel/component/mllp/impl/Hl7Util.java  |  91 ---
 .../mllp/impl/MllpBufferedSocketWriter.java        | 124 ----
 .../component/mllp/impl/MllpSocketReader.java      | 290 --------
 .../camel/component/mllp/impl/MllpSocketUtil.java  | 231 ------
 .../component/mllp/impl/MllpSocketWriter.java      | 144 ----
 .../camel/component/mllp/internal/Hl7Util.java     | 336 +++++++++
 .../component/mllp/internal/MllpSocketBuffer.java  | 653 +++++++++++++++++
 .../mllp/internal/TcpServerAcceptRunnable.java     | 150 ++++
 .../mllp/internal/TcpSocketConsumerRunnable.java   | 595 +++++++++++++++
 .../Hl7AcknowledgementGenerationException.java     |   6 +-
 .../mllp/Hl7AcknowledgementGenerator.java          |  40 +-
 .../MllpAcknowledgementDeliveryExceptionTest.java  |  97 +++
 .../mllp/MllpAcknowledgementExceptionTest.java     | 157 ++++
 .../MllpAcknowledgementReceiveExceptionTest.java   | 154 ++++
 .../MllpAcknowledgementTimeoutExceptionTest.java   | 173 +++++
 ...plicationErrorAcknowledgementExceptionTest.java |  76 ++
 ...licationRejectAcknowledgementExceptionTest.java |  76 ++
 ...llpCommitErrorAcknowledgementExceptionTest.java |  76 ++
 ...lpCommitRejectAcknowledgementExceptionTest.java |  75 ++
 .../camel/component/mllp/MllpComponentTest.java    | 132 ++++
 ...ava => MllpConfigurationBeanBlueprintTest.java} |  54 +-
 .../camel/component/mllp/MllpEndpointTest.java     | 195 +++++
 .../camel/component/mllp/MllpExceptionTest.java    | 169 +++--
 .../component/mllp/MllpExceptionTestSupport.java   |  44 ++
 .../MllpInvalidAcknowledgementExceptionTest.java   |  64 ++
 .../mllp/MllpInvalidMessageExceptionTest.java      |  64 ++
 .../MllpNegativeAcknowledgementExceptionTest.java  |  93 +++
 .../mllp/MllpProducerConsumerLoopbackTest.java     |  39 +-
 .../component/mllp/MllpReceiveExceptionTest.java   | 129 ++++
 .../component/mllp/MllpSocketExceptionTest.java    |  61 ++
 .../MllpTcpClientProducerAcknowledgementTest.java  | 286 --------
 ...lientProducerAcknowledgementValidationTest.java | 283 --------
 .../mllp/MllpTcpClientProducerBlueprintTest.java   |  87 +--
 .../MllpTcpClientProducerConnectionErrorTest.java  | 159 +++-
 ...cpClientProducerIdleConnectionTimeoutTest.java} | 115 ++-
 ...roducerOptionalEndOfDataWithValidationTest.java |  81 +++
 ...ucerOptionalEndOfDataWithoutValidationTest.java |  85 +++
 ...roducerRequiredEndOfDataWithValidationTest.java |  86 +++
 ...ucerRequiredEndOfDataWithoutValidationTest.java |  86 +++
 .../component/mllp/MllpTcpClientProducerTest.java  | 230 ------
 .../MllpTcpServerConsumerAcknowledgementTest.java  |  82 +--
 .../mllp/MllpTcpServerConsumerBindTimeoutTest.java |  33 +-
 .../mllp/MllpTcpServerConsumerBlueprintTest.java   |  34 +-
 .../mllp/MllpTcpServerConsumerConnectionTest.java  |  63 +-
 .../MllpTcpServerConsumerMessageHeadersTest.java   |  37 +-
 ...MllpTcpServerConsumerMessageValidationTest.java | 316 --------
 ...MllpTcpServerConsumerMulitpleTcpPacketTest.java |  33 +-
 ...onsumerOptionalEndOfDataWithValidationTest.java |  89 +++
 ...umerOptionalEndOfDataWithoutValidationTest.java | 105 +++
 ...onsumerRequiredEndOfDataWithValidationTest.java |  90 +++
 ...umerRequiredEndOfDataWithoutValidationTest.java | 100 +++
 .../component/mllp/MllpTcpServerConsumerTest.java  | 287 --------
 .../mllp/MllpTcpServerConsumerTransactionTest.java |  64 +-
 .../component/mllp/MllpWriteExceptionTest.java     |  95 +++
 ...tProducerEndOfDataAndValidationTestSupport.java | 512 +++++++++++++
 ...rConsumerEndOfDataAndValidationTestSupport.java | 408 +++++++++++
 .../camel/component/mllp/impl/Hl7UtilTest.java     | 126 ----
 ...llpBufferedSocketAcknowledgementWriterTest.java | 125 ----
 .../impl/MllpBufferedSocketMessageWriterTest.java  | 126 ----
 .../impl/MllpSocketAcknowledgementReaderTest.java  | 533 --------------
 .../impl/MllpSocketAcknowledgementWriterTest.java  | 150 ----
 .../mllp/impl/MllpSocketMessageReaderTest.java     | 527 --------------
 .../mllp/impl/MllpSocketMessageWriterTest.java     | 150 ----
 .../mllp/impl/MllpSocketReaderTestSupport.java     | 342 ---------
 .../impl/MllpSocketUtilExceptionHandlingTest.java  | 133 ----
 .../impl/MllpSocketUtilFindXxxOfBlockTest.java     | 241 ------
 .../mllp/impl/MllpSocketUtilSocketTest.java        | 288 --------
 .../mllp/impl/MllpSocketWriterTestSupport.java     | 132 ----
 .../camel/component/mllp/internal/Hl7UtilTest.java | 514 +++++++++++++
 .../internal/MllpSocketBufferReadFromTest.java     |  89 +++
 .../mllp/internal/MllpSocketBufferTest.java        | 732 +++++++++++++++++++
 .../mllp/internal/MllpSocketBufferWriteTest.java   | 394 ++++++++++
 .../mllp/internal/SocketBufferTestSupport.java     |  68 ++
 .../camel/test/executor/PooledExecutorTest.java    |  73 ++
 .../apache/camel/test/executor/TestExecutor.java   |  56 ++
 .../apache/camel/test/executor/TestRunnable.java   |  61 ++
 .../test/junit/rule/mllp/MllpClientResource.java   |  41 +-
 .../MllpJUnitResourceCorruptFrameException.java    |   1 +
 .../rule/mllp/MllpJUnitResourceException.java      |   1 +
 .../mllp/MllpJUnitResourceTimeoutException.java    |   1 +
 .../test/junit/rule/mllp/MllpServerResource.java   | 408 ++++++-----
 ...Generator.java => Hl7TestMessageGenerator.java} |  45 +-
 .../camel/test/mllp/PassthroughProcessor.java      |   1 +
 .../camel/MllpEndpointStub.java}                   |  26 +-
 .../camel/test/stub/tcp/SocketInputStreamStub.java | 198 +++++
 .../test/stub/tcp/SocketOutputStreamStub.java      |  76 ++
 .../org/apache/camel/test/stub/tcp/SocketStub.java | 186 +++++
 .../org/apache/camel/test/tcp/JavaSocketTests.java | 135 +++-
 .../org/apache/camel/test/util/PayloadBuilder.java | 113 +--
 ...r-test.xml => mllp-configuration-bean-test.xml} |  21 +-
 .../blueprint/mllp-tcp-client-producer-test.xml    |  16 +-
 .../blueprint/mllp-tcp-server-consumer-test.xml    |   2 +-
 119 files changed, 10363 insertions(+), 7236 deletions(-)

diff --git a/components/camel-mllp/pom.xml b/components/camel-mllp/pom.xml
index 1368711..4712244 100644
--- a/components/camel-mllp/pom.xml
+++ b/components/camel-mllp/pom.xml
@@ -96,4 +96,19 @@
 
   </dependencies>
 
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <childDelegation>false</childDelegation>
+          <useFile>true</useFile>
+          <forkCount>1</forkCount>
+          <reuseForks>true</reuseForks>
+          <forkedProcessTimeoutInSeconds>600</forkedProcessTimeoutInSeconds>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
 </project>
diff --git a/components/camel-mllp/src/data/mmodal_msg.txt b/components/camel-mllp/src/data/mmodal_msg.txt
new file mode 100644
index 0000000..5599119
--- /dev/null
+++ b/components/camel-mllp/src/data/mmodal_msg.txt
@@ -0,0 +1,39 @@
+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
new file mode 100644
index 0000000..7209d69
--- /dev/null
+++ b/components/camel-mllp/src/data/prod-error-2017-11-07.txt
@@ -0,0 +1,13 @@
+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/MllpAcknowledgementDeliveryException.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpAcknowledgementDeliveryException.java
index 19bc353..d6b8287 100644
--- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpAcknowledgementDeliveryException.java
+++ b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpAcknowledgementDeliveryException.java
@@ -14,13 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.camel.component.mllp;
 
 /**
  * Raised when a MLLP Consumer cannot deliver the MLLP Acknowledgement
  */
 public class MllpAcknowledgementDeliveryException extends MllpAcknowledgementException {
-
     static final String EXCEPTION_MESSAGE = "HL7 Acknowledgment Delivery Failed";
 
     public MllpAcknowledgementDeliveryException(byte[] hl7Message, byte[] hl7Acknowledgement) {
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpAcknowledgementException.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpAcknowledgementException.java
index 296430b..af21a00 100644
--- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpAcknowledgementException.java
+++ b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpAcknowledgementException.java
@@ -14,10 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.camel.component.mllp;
 
 /**
- * Base class for HL7 Application Acknowledgement Exceptions
+ * Base Exception for with HL7 Application Acknowledgements
  */
 public abstract class MllpAcknowledgementException extends MllpException {
 
@@ -25,6 +26,10 @@ public abstract class MllpAcknowledgementException extends MllpException {
         super(message);
     }
 
+    public MllpAcknowledgementException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
     public MllpAcknowledgementException(String message, byte[] hl7Message) {
         super(message, hl7Message);
     }
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpReceiveAcknowledgementException.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpAcknowledgementReceiveException.java
similarity index 75%
rename from components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpReceiveAcknowledgementException.java
rename to components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpAcknowledgementReceiveException.java
index 75b486f..2f3977c 100644
--- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpReceiveAcknowledgementException.java
+++ b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpAcknowledgementReceiveException.java
@@ -14,43 +14,51 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.camel.component.mllp;
 
 /**
  * Raised when a MLLP Consumer cannot deliver the MLLP Acknowledgement
  */
-public class MllpReceiveAcknowledgementException extends MllpAcknowledgementException {
+public class MllpAcknowledgementReceiveException extends MllpAcknowledgementException {
     static final String EXCEPTION_MESSAGE = "HL7 Acknowledgment Receipt Failed";
 
-    public MllpReceiveAcknowledgementException(byte[] hl7Message) {
+    public MllpAcknowledgementReceiveException(byte[] hl7Message) {
         super(EXCEPTION_MESSAGE, hl7Message);
     }
 
-    public MllpReceiveAcknowledgementException(byte[] hl7Message, byte[] hl7Acknowledgement) {
+
+    public MllpAcknowledgementReceiveException(byte[] hl7Message, byte[] hl7Acknowledgement) {
         super(EXCEPTION_MESSAGE, hl7Message, hl7Acknowledgement);
     }
 
-    public MllpReceiveAcknowledgementException(byte[] hl7Message, Throwable cause) {
+
+    public MllpAcknowledgementReceiveException(byte[] hl7Message, Throwable cause) {
         super(EXCEPTION_MESSAGE, hl7Message, cause);
     }
 
-    public MllpReceiveAcknowledgementException(byte[] hl7Message, byte[] hl7Acknowledgement, Throwable cause) {
+
+    public MllpAcknowledgementReceiveException(byte[] hl7Message, byte[] hl7Acknowledgement, Throwable cause) {
         super(EXCEPTION_MESSAGE, hl7Message, hl7Acknowledgement, cause);
     }
 
-    public MllpReceiveAcknowledgementException(String message, byte[] hl7Message) {
+
+    public MllpAcknowledgementReceiveException(String message, byte[] hl7Message) {
         super(message, hl7Message);
     }
 
-    public MllpReceiveAcknowledgementException(String message, byte[] hl7Message, byte[] hl7Acknowledgement) {
+
+    public MllpAcknowledgementReceiveException(String message, byte[] hl7Message, byte[] hl7Acknowledgement) {
         super(message, hl7Message, hl7Acknowledgement);
     }
 
-    public MllpReceiveAcknowledgementException(String message, byte[] hl7Message, Throwable cause) {
+
+    public MllpAcknowledgementReceiveException(String message, byte[] hl7Message, Throwable cause) {
         super(message, hl7Message, cause);
     }
 
-    public MllpReceiveAcknowledgementException(String message, byte[] hl7Message, byte[] hl7Acknowledgement, Throwable cause) {
+
+    public MllpAcknowledgementReceiveException(String message, byte[] hl7Message, byte[] hl7Acknowledgement, Throwable cause) {
         super(message, hl7Message, hl7Acknowledgement, cause);
     }
 }
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpAcknowledgementTimeoutException.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpAcknowledgementTimeoutException.java
index 50510c9..9c21a48 100644
--- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpAcknowledgementTimeoutException.java
+++ b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpAcknowledgementTimeoutException.java
@@ -14,13 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.camel.component.mllp;
 
 /**
  * Raised when a MLLP Producer does not receive a HL7 acknowledgement within the configured timespan
  */
 public class MllpAcknowledgementTimeoutException extends MllpAcknowledgementException {
-
     static final String EXCEPTION_MESSAGE = "Timeout receiving HL7 Acknowledgement";
 
     public MllpAcknowledgementTimeoutException(byte[] hl7Message) {
@@ -58,13 +58,12 @@ public class MllpAcknowledgementTimeoutException extends MllpAcknowledgementExce
     /**
      * Get the HL7 acknowledgement payload associated with this exception, if any.
      *
-     * @return If the timeout occurred while attempting to receive an HL7 Message, this will be null.  If the timeout
-     * occurred while attempting to receive an HL7 Acknowledgement, this will be the HL7 Message.  If the timeout occurred
-     * while attempting to complete the read of an HL7 message (i.e. part of the message has already been read), this
-     * will be the partial acknowledgement payload that was read before the timeout.
+     * @return If the timeout occurred while attempting to receive an HL7 Message, this will be null.  If the timeout occurred while attempting to receive an HL7 Acknowledgement, this will be the HL7
+     * Message.  If the timeout occurred while attempting to complete the read of an HL7 message (i.e. part of the message has already been read), this will be the partial acknowledgement payload that
+     * was read before the timeout.
      */
     public byte[] getHl7Acknowledgement() {
-        return super.getHl7Acknowledgement();
+        return super.getHl7AcknowledgementBytes();
     }
 
 }
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpApplicationErrorAcknowledgementException.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpApplicationErrorAcknowledgementException.java
index afcbdc5..d7621dd 100644
--- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpApplicationErrorAcknowledgementException.java
+++ b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpApplicationErrorAcknowledgementException.java
@@ -14,13 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.camel.component.mllp;
 
 /**
  * Raised when a MLLP Producer receives a HL7 Application Error Acknowledgement
  */
 public class MllpApplicationErrorAcknowledgementException extends MllpNegativeAcknowledgementException {
-
     static final String EXCEPTION_MESSAGE = "HL7 Application Error Acknowledgment Received";
 
     public MllpApplicationErrorAcknowledgementException(byte[] hl7Message, byte[] hl7Acknowledgement) {
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpApplicationRejectAcknowledgementException.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpApplicationRejectAcknowledgementException.java
index ca0deb6..d11a372 100644
--- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpApplicationRejectAcknowledgementException.java
+++ b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpApplicationRejectAcknowledgementException.java
@@ -14,13 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.camel.component.mllp;
 
 /**
  * Raised when a MLLP Producer receives a HL7 Application Reject Acknowledgement
  */
 public class MllpApplicationRejectAcknowledgementException extends MllpNegativeAcknowledgementException {
-
     static final String EXCEPTION_MESSAGE = "HL7 Application Reject Acknowledgment Received";
 
     public MllpApplicationRejectAcknowledgementException(byte[] hl7Message, byte[] hl7Acknowledgement) {
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpCommitErrorAcknowledgementException.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpCommitErrorAcknowledgementException.java
index 05591de..783430f 100644
--- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpCommitErrorAcknowledgementException.java
+++ b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpCommitErrorAcknowledgementException.java
@@ -14,13 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.camel.component.mllp;
 
 /**
  * Raised when a MLLP Producer receives a HL7 Commit Error Acknowledgement
  */
 public class MllpCommitErrorAcknowledgementException extends MllpNegativeAcknowledgementException {
-
     static final String EXCEPTION_MESSAGE = "HL7 Commit Error Acknowledgment Received";
 
     public MllpCommitErrorAcknowledgementException(byte[] hl7Message, byte[] hl7Acknowledgement) {
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpCommitRejectAcknowledgementException.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpCommitRejectAcknowledgementException.java
index b21d213..6007ccf 100644
--- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpCommitRejectAcknowledgementException.java
+++ b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpCommitRejectAcknowledgementException.java
@@ -14,13 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.camel.component.mllp;
 
 /**
  * Raised when a MLLP Producer receives a HL7 Commit Reject Acknowledgement
  */
 public class MllpCommitRejectAcknowledgementException extends MllpNegativeAcknowledgementException {
-
     static final String EXCEPTION_MESSAGE = "HL7 Commit Reject Acknowledgment Received";
 
     public MllpCommitRejectAcknowledgementException(byte[] hl7Message, byte[] hl7Acknowledgement) {
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpComponent.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpComponent.java
index 504a6cf..4525e08 100644
--- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpComponent.java
+++ b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpComponent.java
@@ -14,6 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.camel.component.mllp;
 
 import java.util.Map;
@@ -21,16 +22,23 @@ import java.util.Map;
 import org.apache.camel.CamelContext;
 import org.apache.camel.Endpoint;
 import org.apache.camel.impl.UriEndpointComponent;
-
-import static org.apache.camel.component.mllp.MllpEndpoint.END_OF_BLOCK;
-import static org.apache.camel.component.mllp.MllpEndpoint.START_OF_BLOCK;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Represents the component that manages {@link MllpEndpoint}.
  */
 public class MllpComponent extends UriEndpointComponent {
-
     public static final String MLLP_LOG_PHI_PROPERTY = "org.apache.camel.component.mllp.logPHI";
+    public static final String MLLP_LOG_PHI_MAX_BYTES_PROPERTY = "org.apache.camel.component.mllp.logPHI.maxBytes";
+    public static final boolean DEFAULT_LOG_PHI = true;
+    public static final int DEFAULT_LOG_PHI_MAX_BYTES = 5120;
+
+    static Logger log = LoggerFactory.getLogger(MllpComponent.class);
+    static Boolean logPhi;
+    static Integer logPhiMaxBytes;
+
+    MllpConfiguration configuration;
 
     public MllpComponent() {
         super(MllpEndpoint.class);
@@ -40,66 +48,89 @@ public class MllpComponent extends UriEndpointComponent {
         super(context, MllpEndpoint.class);
     }
 
-    protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception {
-        MllpEndpoint endpoint = new MllpEndpoint(uri, this);
-        setProperties(endpoint, parameters);
+    @Override
+    protected Endpoint createEndpoint(String uriString, String remaining, Map<String, Object> parameters) throws Exception {
+        MllpEndpoint endpoint = new MllpEndpoint(uriString, this, hasConfiguration() ? configuration.copy() : new MllpConfiguration());
 
-        // mllp://hostname:port
-        String hostPort;
-        // look for options
-        int optionsStartIndex = uri.indexOf('?');
-        if (-1 == optionsStartIndex) {
-            // No options - just get the host/port stuff
-            hostPort = uri.substring(7);
-        } else {
-            hostPort = uri.substring(7, optionsStartIndex);
-        }
+        endpoint.setBridgeErrorHandler(true);
+
+        setProperties(endpoint, parameters);
 
         // Make sure it has a host - may just be a port
-        int colonIndex = hostPort.indexOf(':');
+        int colonIndex = remaining.indexOf(':');
         if (-1 != colonIndex) {
-            endpoint.setHostname(hostPort.substring(0, colonIndex));
-            endpoint.setPort(Integer.parseInt(hostPort.substring(colonIndex + 1)));
+            endpoint.setHostname(remaining.substring(0, colonIndex));
+            endpoint.setPort(Integer.parseInt(remaining.substring(colonIndex + 1)));
         } else {
             // No host specified - leave the default host and set the port
-            endpoint.setPort(Integer.parseInt(hostPort.substring(colonIndex + 1)));
+            endpoint.setPort(Integer.parseInt(remaining));
         }
 
         return endpoint;
     }
 
-    public static boolean isLogPhi() {
-        String logPhiProperty = System.getProperty(MllpComponent.MLLP_LOG_PHI_PROPERTY, "true");
-        return Boolean.valueOf(logPhiProperty);
+    public static boolean hasLogPhi() {
+        return logPhi != null;
     }
 
-    public static String covertToPrintFriendlyString(String hl7Message) {
-        if (hl7Message == null) {
-            return "null";
-        } else if (hl7Message.isEmpty()) {
-            return "empty";
+    public static boolean isLogPhi() {
+        if (hasLogPhi()) {
+            return logPhi;
         }
 
-        return hl7Message.replaceAll("" + START_OF_BLOCK, "<VT>").replaceAll("" + END_OF_BLOCK, "<FS>").replaceAll("\r", "<CR>").replaceAll("\n", "<LF>");
-    }
+        boolean answer = DEFAULT_LOG_PHI;
+        String logPhiProperty = System.getProperty(MllpComponent.MLLP_LOG_PHI_PROPERTY);
 
-    public static String covertBytesToPrintFriendlyString(byte[] hl7Bytes) {
-        if (hl7Bytes == null) {
-            return "null";
-        } else if (hl7Bytes.length == 0) {
-            return "";
+        if (logPhiProperty != null) {
+            answer = Boolean.valueOf(logPhiProperty);
         }
 
-        return covertBytesToPrintFriendlyString(hl7Bytes, 0, hl7Bytes.length);
+        return answer;
+    }
+
+    public static void setLogPhi(Boolean logPhi) {
+        MllpComponent.logPhi = logPhi;
     }
 
-    public static String covertBytesToPrintFriendlyString(byte[] hl7Bytes, int startPosition, int length) {
-        if (null == hl7Bytes) {
-            return "null";
-        } else if (hl7Bytes.length == 0) {
-            return "";
+    public static boolean hasLogPhiMaxBytes() {
+        return logPhiMaxBytes != null;
+    }
+
+    public static int getLogPhiMaxBytes() {
+        if (hasLogPhiMaxBytes()) {
+            return logPhiMaxBytes;
+        }
+
+        int answer = DEFAULT_LOG_PHI_MAX_BYTES;
+        String logPhiProperty = System.getProperty(MllpComponent.MLLP_LOG_PHI_MAX_BYTES_PROPERTY);
+
+        if (logPhiProperty != null && !logPhiProperty.isEmpty()) {
+            try {
+                answer = Integer.valueOf(logPhiProperty);
+            } catch (NumberFormatException numberFormatException) {
+                log.warn("Invalid Interger value '{}' for system property {} - using default value of {}", logPhiProperty, MllpComponent.MLLP_LOG_PHI_MAX_BYTES_PROPERTY, answer);
+                // use DEFAULT_LOG_PHI_MAX_BYTES for a invalid entry
+            }
         }
-        return covertToPrintFriendlyString(new String(hl7Bytes, startPosition, length));
+
+        return answer;
+    }
+
+    public static void setLogPhiMaxBytes(Integer logPhiMaxBytes) {
+        MllpComponent.logPhiMaxBytes = logPhiMaxBytes;
     }
 
+
+
+    public boolean hasConfiguration() {
+        return configuration != null;
+    }
+
+    public MllpConfiguration getConfiguration() {
+        return configuration;
+    }
+
+    public void setConfiguration(MllpConfiguration configuration) {
+        this.configuration = configuration;
+    }
 }
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpConfiguration.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpConfiguration.java
new file mode 100644
index 0000000..70a756b
--- /dev/null
+++ b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpConfiguration.java
@@ -0,0 +1,603 @@
+/**
+ * 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.Objects;
+
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.spi.UriParams;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The MLLP Component configuration.
+ */
+@UriParams
+public class MllpConfiguration implements Cloneable {
+    static final Logger LOG = LoggerFactory.getLogger(MllpConfiguration.class);
+
+    @UriParam(label = "advanced,consumer,tcp", defaultValue = "5")
+    Integer backlog = 5;
+
+    @UriParam(label = "advanced,consumer,tcp,timeout", defaultValue = "30000")
+    int bindTimeout = 30000;
+
+    @UriParam(label = "advanced,consumer,tcp,timeout", defaultValue = "5000")
+    int bindRetryInterval = 5000;
+
+    @UriParam(label = "advanced,consumer,tcp,timeout", defaultValue = "60000")
+    int acceptTimeout = 60000;
+
+    @UriParam(label = "advanced,producer,tcp,timeout", defaultValue = "30000")
+    int connectTimeout = 30000;
+
+    @UriParam(label = "advanced,tcp,timeout", defaultValue = "15000")
+    int receiveTimeout = 15000;
+
+    @UriParam(label = "advanced,consumer,tcp", defaultValue = "5")
+    int maxConcurrentConsumers = 5;
+
+    @Deprecated // use idleTimeout
+    @UriParam(label = "advanced,consumer,tcp,timeout", defaultValue = "null")
+    Integer maxReceiveTimeouts;
+
+    @UriParam(label = "advanced,tcp,timeout", defaultValue = "null")
+    Integer idleTimeout;
+
+    @UriParam(label = "advanced,tcp,timeout", defaultValue = "500")
+    int readTimeout = 500;
+
+    @UriParam(label = "advanced,producer,tcp", defaultValue = "true")
+    Boolean keepAlive = true;
+
+    @UriParam(label = "advanced,producer,tcp", defaultValue = "true")
+    Boolean tcpNoDelay = true;
+
+    @UriParam(label = "advanced,consumer,tcp", defaultValue = "true")
+    Boolean reuseAddress = true;
+
+    @UriParam(label = "advanced,tcp", defaultValue = "8192")
+    Integer receiveBufferSize = 8192;
+
+    @UriParam(label = "advanced,tcp", defaultValue = "8192")
+    Integer sendBufferSize = 8192;
+
+    @UriParam(defaultValue = "true")
+    boolean autoAck = true;
+
+    @UriParam(defaultValue = "true")
+    boolean hl7Headers = true;
+
+    @UriParam(defaultValue = "false")
+    @Deprecated
+    boolean bufferWrites;
+
+    @UriParam(defaultValue = "true")
+    boolean requireEndOfData = true;
+
+    @UriParam(defaultValue = "true")
+    boolean stringPayload = true;
+
+    @UriParam(defaultValue = "false")
+    boolean validatePayload;
+
+    @UriParam(label = "codec")
+    String charsetName;
+
+    public MllpConfiguration() {
+    }
+
+    public MllpConfiguration(MllpConfiguration source) {
+        this.copy(source);
+    }
+
+    public static void copy(MllpConfiguration source, MllpConfiguration target) {
+        if (source == null) {
+            LOG.warn("Values were not copied by MllpConfiguration.copy(MllpConfiguration source, MllpConfiguration target) - source argument is null");
+        } else if (target == null) {
+            LOG.warn("Values were not copied by MllpConfiguration.copy(MllpConfiguration source, MllpConfiguration target) - target argument is null");
+        } else {
+            target.backlog = source.backlog;
+            target.bindTimeout = source.bindTimeout;
+            target.bindRetryInterval = source.bindRetryInterval;
+            target.acceptTimeout = source.acceptTimeout;
+            target.connectTimeout = source.connectTimeout;
+            target.receiveTimeout = source.receiveTimeout;
+            target.idleTimeout = source.idleTimeout;
+            target.readTimeout = source.readTimeout;
+            target.keepAlive = source.keepAlive;
+            target.tcpNoDelay = source.tcpNoDelay;
+            target.reuseAddress = source.reuseAddress;
+            target.receiveBufferSize = source.receiveBufferSize;
+            target.sendBufferSize = source.sendBufferSize;
+            target.autoAck = source.autoAck;
+            target.hl7Headers = source.hl7Headers;
+            target.bufferWrites = source.bufferWrites;
+            target.requireEndOfData = source.requireEndOfData;
+            target.stringPayload = source.stringPayload;
+            target.validatePayload = source.validatePayload;
+            target.charsetName = source.charsetName;
+        }
+    }
+
+    public MllpConfiguration copy() {
+        MllpConfiguration target = new MllpConfiguration();
+
+        MllpConfiguration.copy(this, target);
+
+        return target;
+    }
+
+    public void copy(MllpConfiguration source) {
+        MllpConfiguration.copy(source, this);
+    }
+
+    public boolean hasCharsetName() {
+        return charsetName != null && !charsetName.isEmpty();
+    }
+
+    public String getCharsetName() {
+        return charsetName;
+    }
+
+    /**
+     * Set the CamelCharsetName property on the exchange
+     *
+     * @param charsetName the charset
+     */
+    public void setCharsetName(String charsetName) {
+        this.charsetName = charsetName;
+    }
+
+    public boolean hasBacklog() {
+        return backlog != null && backlog > 0;
+    }
+
+    public Integer getBacklog() {
+        return backlog;
+    }
+
+    /**
+     * The maximum queue length for incoming connection indications (a request to connect) is set to the backlog parameter. If a connection indication arrives when the queue is full, the connection is
+     * refused.
+     */
+    public void setBacklog(Integer backlog) {
+        this.backlog = backlog;
+    }
+
+    public int getBindTimeout() {
+        return bindTimeout;
+    }
+
+    /**
+     * TCP Server Only - The number of milliseconds to retry binding to a server port
+     */
+    public void setBindTimeout(int bindTimeout) {
+        this.bindTimeout = bindTimeout;
+    }
+
+    public int getBindRetryInterval() {
+        return bindRetryInterval;
+    }
+
+    /**
+     * TCP Server Only - The number of milliseconds to wait between bind attempts
+     */
+    public void setBindRetryInterval(int bindRetryInterval) {
+        this.bindRetryInterval = bindRetryInterval;
+    }
+
+    public int getAcceptTimeout() {
+        return acceptTimeout;
+    }
+
+    /**
+     * Timeout (in milliseconds) while waiting for a TCP connection
+     * <p/>
+     * TCP Server Only
+     *
+     * @param acceptTimeout timeout in milliseconds
+     */
+    public void setAcceptTimeout(int acceptTimeout) {
+        this.acceptTimeout = acceptTimeout;
+    }
+
+    public int getConnectTimeout() {
+        return connectTimeout;
+    }
+
+    /**
+     * Timeout (in milliseconds) for establishing for a TCP connection
+     * <p/>
+     * TCP Client only
+     *
+     * @param connectTimeout timeout in milliseconds
+     */
+    public void setConnectTimeout(int connectTimeout) {
+        this.connectTimeout = connectTimeout;
+    }
+
+    public int getReceiveTimeout() {
+        return receiveTimeout;
+    }
+
+    /**
+     * The SO_TIMEOUT value (in milliseconds) used when waiting for the start of an MLLP frame
+     *
+     * @param receiveTimeout timeout in milliseconds
+     */
+    public void setReceiveTimeout(int receiveTimeout) {
+        this.receiveTimeout = receiveTimeout;
+    }
+
+    public int getMaxConcurrentConsumers() {
+        return maxConcurrentConsumers;
+    }
+
+    /**
+     * The maximum number of concurrent MLLP Consumer connections that will be allowed.  If a new
+     * connection is received and the maximum is number are already established, the new connection will be reset immediately.
+     *
+     * @param maxConcurrentConsumers the maximum number of concurrent consumer connections allowed
+     */
+    public void setMaxConcurrentConsumers(int maxConcurrentConsumers) {
+        this.maxConcurrentConsumers = maxConcurrentConsumers;
+    }
+
+    /**
+     * Determine if the maxReceiveTimeouts URI parameter has been set
+     *
+     * @return true if the parameter has been set; false otherwise
+     *
+     * @deprecated Use the idleTimeout URI parameter
+     */
+    public boolean hasMaxReceiveTimeouts() {
+        return maxReceiveTimeouts != null;
+    }
+
+    /**
+     * Retrieve the value of the maxReceiveTimeouts URI parameter.
+     *
+     * @return the maximum number of receive timeouts before the TCP Socket is reset
+     *
+     * @deprecated Use the idleTimeout URI parameter
+     */
+    public Integer getMaxReceiveTimeouts() {
+        return maxReceiveTimeouts;
+    }
+
+    /**
+     * The maximum number of timeouts (specified by receiveTimeout) allowed before the TCP Connection will be reset.
+     *
+     * @param maxReceiveTimeouts maximum number of receiveTimeouts
+     *
+     * @deprecated Use the idleTimeout URI parameter.  For backward compibility, setting this parameter will result in an
+     * idle timeout of maxReceiveTimeouts * receiveTimeout.  If idleTimeout is also specified, this parameter will be ignored.
+     */
+    public void setMaxReceiveTimeouts(Integer maxReceiveTimeouts) {
+        this.maxReceiveTimeouts = maxReceiveTimeouts;
+    }
+
+    public boolean hasIdleTimeout() {
+        return idleTimeout != null && idleTimeout > 0;
+    }
+
+    public Integer getIdleTimeout() {
+        return idleTimeout;
+    }
+
+    /**
+     * The approximate idle time allowed before the Client TCP Connection will be reset.
+     *
+     * A null value or a value less than or equal to zero will disable the idle timeout.
+     *
+     * @param idleTimeout timeout in milliseconds
+     */
+    public void setIdleTimeout(Integer idleTimeout) {
+        this.idleTimeout = idleTimeout;
+    }
+
+    public int getReadTimeout() {
+        return readTimeout;
+    }
+
+    /**
+     * The SO_TIMEOUT value (in milliseconds) used after the start of an MLLP frame has been received
+     *
+     * @param readTimeout timeout in milliseconds
+     */
+    public void setReadTimeout(int readTimeout) {
+        this.readTimeout = readTimeout;
+    }
+
+    public boolean hasKeepAlive() {
+        return keepAlive != null;
+    }
+
+    public Boolean getKeepAlive() {
+        return keepAlive;
+    }
+
+    /**
+     * Enable/disable the SO_KEEPALIVE socket option.
+     *
+     * @param keepAlive enable SO_KEEPALIVE when true; disable SO_KEEPALIVE when false; use system default when null
+     */
+    public void setKeepAlive(Boolean keepAlive) {
+        this.keepAlive = keepAlive;
+    }
+
+    public boolean hasTcpNoDelay() {
+        return tcpNoDelay != null;
+    }
+
+    public Boolean getTcpNoDelay() {
+        return tcpNoDelay;
+    }
+
+    /**
+     * Enable/disable the TCP_NODELAY socket option.
+     *
+     * @param tcpNoDelay enable TCP_NODELAY when true; disable TCP_NODELAY when false; use system default when null
+     */
+    public void setTcpNoDelay(Boolean tcpNoDelay) {
+        this.tcpNoDelay = tcpNoDelay;
+    }
+
+    public boolean hasReuseAddress() {
+        return reuseAddress != null;
+    }
+
+    public Boolean getReuseAddress() {
+        return reuseAddress;
+    }
+
+    /**
+     * Enable/disable the SO_REUSEADDR socket option.
+     *
+     * @param reuseAddress enable SO_REUSEADDR when true; disable SO_REUSEADDR when false; use system default when null
+     */
+    public void setReuseAddress(boolean reuseAddress) {
+        this.reuseAddress = reuseAddress;
+    }
+
+    public boolean hasReceiveBufferSize() {
+        return receiveBufferSize != null && receiveBufferSize > 0;
+    }
+
+    public int getReceiveBufferSize() {
+        return receiveBufferSize;
+    }
+
+    /**
+     * Sets the SO_RCVBUF option to the specified value (in bytes)
+     *
+     * @param receiveBufferSize the SO_RCVBUF option value.  If null, the system default is used
+     */
+    public void setReceiveBufferSize(Integer receiveBufferSize) {
+        this.receiveBufferSize = receiveBufferSize;
+    }
+
+    public boolean hasSendBufferSize() {
+        return sendBufferSize != null && sendBufferSize > 0;
+    }
+
+    public int getSendBufferSize() {
+        return sendBufferSize;
+    }
+
+    /**
+     * Sets the SO_SNDBUF option to the specified value (in bytes)
+     *
+     * @param sendBufferSize the SO_SNDBUF option value.  If null, the system default is used
+     */
+    public void setSendBufferSize(Integer sendBufferSize) {
+        this.sendBufferSize = sendBufferSize;
+    }
+
+    public boolean isAutoAck() {
+        return autoAck;
+    }
+
+    /**
+     * Enable/Disable the automatic generation of a MLLP Acknowledgement
+     *
+     * MLLP Consumers only
+     *
+     * @param autoAck enabled if true, otherwise disabled
+     */
+    public void setAutoAck(boolean autoAck) {
+        this.autoAck = autoAck;
+    }
+
+    public boolean isHl7Headers() {
+        return hl7Headers;
+    }
+
+    public boolean getHl7Headers() {
+        return isHl7Headers();
+    }
+
+    /**
+     * Enable/Disable the automatic generation of message headers from the HL7 Message
+     *
+     * MLLP Consumers only
+     *
+     * @param hl7Headers enabled if true, otherwise disabled
+     */
+    public void setHl7Headers(boolean hl7Headers) {
+        this.hl7Headers = hl7Headers;
+    }
+
+    public boolean isRequireEndOfData() {
+        return requireEndOfData;
+    }
+
+    /**
+     * Enable disable strict compliance to the MLLP standard.
+     *
+     * The MLLP standard specifies [START_OF_BLOCK]hl7 payload[END_OF_BLOCK][END_OF_DATA], however, some systems do not send
+     * the final END_OF_DATA byte.  This setting controls whether or not the final END_OF_DATA byte is required or optional.
+     *
+     * @param requireEndOfData the trailing END_OF_DATA byte is required if true; optional otherwise
+     */
+    public void setRequireEndOfData(boolean requireEndOfData) {
+        this.requireEndOfData = requireEndOfData;
+    }
+
+    public boolean isStringPayload() {
+        return stringPayload;
+    }
+
+    /**
+     * Enable/Disable converting the payload to a String.
+     *
+     * If enabled, HL7 Payloads received from external systems will be validated converted to a String.
+     *
+     * If the charsetName property is set, that character set will be used for the conversion.  If the charsetName property is
+     * not set, the value of MSH-18 will be used to determine th appropriate character set.  If MSH-18 is not set, then
+     * the default ASCII character set will be use.
+     *
+     * @param stringPayload enabled if true, otherwise disabled
+     */
+    public void setStringPayload(boolean stringPayload) {
+        this.stringPayload = stringPayload;
+    }
+
+    public boolean isValidatePayload() {
+        return validatePayload;
+    }
+
+    /**
+     * Enable/Disable the validation of HL7 Payloads
+     *
+     * If enabled, HL7 Payloads received from external systems will be validated (see Hl7Util.generateInvalidPayloadExceptionMessage
+     * for details on the validation). If and invalid payload is detected, a MllpInvalidMessageException (for consumers) or
+     * a MllpInvalidAcknowledgementException will be thrown.
+     *
+     * @param validatePayload enabled if true, otherwise disabled
+     */
+    public void setValidatePayload(boolean validatePayload) {
+        this.validatePayload = validatePayload;
+    }
+
+    public boolean isBufferWrites() {
+        return bufferWrites;
+    }
+
+    /**
+     * Enable/Disable the validation of HL7 Payloads
+     *
+     * @deprecated the parameter will be ignored
+     *
+     * @param bufferWrites enabled if true, otherwise disabled
+     */
+    public void setBufferWrites(boolean bufferWrites) {
+        this.bufferWrites = bufferWrites;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(backlog,
+            bindTimeout,
+            bindRetryInterval,
+            acceptTimeout,
+            connectTimeout,
+            receiveTimeout,
+            maxConcurrentConsumers,
+            maxReceiveTimeouts,
+            idleTimeout,
+            readTimeout,
+            keepAlive,
+            tcpNoDelay,
+            reuseAddress,
+            receiveBufferSize,
+            sendBufferSize,
+            autoAck,
+            hl7Headers,
+            bufferWrites,
+            requireEndOfData,
+            stringPayload,
+            validatePayload,
+            charsetName);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof MllpConfiguration)) {
+            return false;
+        }
+
+        MllpConfiguration rhs = (MllpConfiguration) o;
+
+        return bindTimeout == rhs.bindTimeout
+            && bindRetryInterval == rhs.bindRetryInterval
+            && acceptTimeout == rhs.acceptTimeout
+            && connectTimeout == rhs.connectTimeout
+            && receiveTimeout == rhs.receiveTimeout
+            && readTimeout == rhs.readTimeout
+            && autoAck == rhs.autoAck
+            && hl7Headers == rhs.hl7Headers
+            && bufferWrites == rhs.bufferWrites
+            && requireEndOfData == rhs.requireEndOfData
+            && stringPayload == rhs.stringPayload
+            && validatePayload == rhs.validatePayload
+            && Objects.equals(backlog, rhs.backlog)
+            && Objects.equals(maxConcurrentConsumers, rhs.maxConcurrentConsumers)
+            && Objects.equals(maxReceiveTimeouts, rhs.maxReceiveTimeouts)
+            && Objects.equals(idleTimeout, rhs.idleTimeout)
+            && Objects.equals(keepAlive, rhs.keepAlive)
+            && Objects.equals(tcpNoDelay, rhs.tcpNoDelay)
+            && Objects.equals(reuseAddress, rhs.reuseAddress)
+            && Objects.equals(receiveBufferSize, rhs.receiveBufferSize)
+            && Objects.equals(sendBufferSize, rhs.sendBufferSize)
+            && Objects.equals(charsetName, rhs.charsetName);
+    }
+
+    @Override
+    public String toString() {
+        return "MllpConfiguration{"
+            + "backlog=" + backlog
+            + ", bindTimeout=" + bindTimeout
+            + ", bindRetryInterval=" + bindRetryInterval
+            + ", acceptTimeout=" + acceptTimeout
+            + ", connectTimeout=" + connectTimeout
+            + ", receiveTimeout=" + receiveTimeout
+            + ", maxConcurrentConsumers=" + maxConcurrentConsumers
+            + ", maxReceiveTimeouts=" + maxReceiveTimeouts
+            + ", idleTimeout=" + idleTimeout
+            + ", readTimeout=" + readTimeout
+            + ", keepAlive=" + keepAlive
+            + ", tcpNoDelay=" + tcpNoDelay
+            + ", reuseAddress=" + reuseAddress
+            + ", receiveBufferSize=" + receiveBufferSize
+            + ", sendBufferSize=" + sendBufferSize
+            + ", autoAck=" + autoAck
+            + ", hl7Headers=" + hl7Headers
+            + ", bufferWrites=" + bufferWrites
+            + ", requireEndOfData=" + requireEndOfData
+            + ", stringPayload=" + stringPayload
+            + ", validatePayload=" + validatePayload
+            + ", charsetName='" + charsetName + '\''
+            + '}';
+    }
+}
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpConstants.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpConstants.java
index 4e14da0..a2d9ae7 100644
--- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpConstants.java
+++ b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpConstants.java
@@ -14,8 +14,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.camel.component.mllp;
 
+/**
+ * Constants for the MLLP Protocol and the Camel MLLP component.
+ */
 public final class MllpConstants {
     public static final String MLLP_LOCAL_ADDRESS = "CamelMllpLocalAddress";
     public static final String MLLP_REMOTE_ADDRESS = "CamelMllpRemoteAddress";
@@ -63,5 +67,7 @@ public final class MllpConstants {
     // MSH-18
     public static final String MLLP_CHARSET = "CamelMllpCharset";
 
-    private MllpConstants() { }
+    private MllpConstants() {
+        //utility class, never constructed
+    }
 }
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 efc0193..ec7a347 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
@@ -14,13 +14,24 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.camel.component.mllp;
 
+import java.io.IOException;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
 import org.apache.camel.Consumer;
 import org.apache.camel.Exchange;
 import org.apache.camel.ExchangePattern;
 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.impl.DefaultEndpoint;
 import org.apache.camel.spi.Metadata;
@@ -31,91 +42,60 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * Provides functionality required by Healthcare providers to communicate with other systems using the MLLP protocol.
+ * The MLLP component is designed to handle the MLLP protocol and provide the functionality required by Healthcare providers to communicate with other systems using the MLLP protocol.
+ *
+ * <p/>
+ * NOTE: MLLP payloads are not logged unless the logging level is set to DEBUG or TRACE to avoid introducing PHI into the log files.  Logging of PHI can be globally disabled by setting the
+ * org.apache.camel.mllp.logPHI system property to false.
  * <p/>
- * MLLP payloads are not logged unless the logging level is set to DEBUG or TRACE to avoid introducing PHI
- * into the log files. Logging of PHI can be globally disabled by setting the org.apache.camel.mllp.logPHI system
- * property to false.
  */
-@ManagedResource(description = "Mllp Endpoint")
-@UriEndpoint(firstVersion = "2.17.0", scheme = "mllp", title = "MLLP", syntax = "mllp:hostname:port", consumerClass = MllpTcpServerConsumer.class, label = "hl7")
+@ManagedResource(description = "MLLP Endpoint")
+@UriEndpoint(scheme = "mllp", title = "MLLP", syntax = "mllp:hostname:port", consumerClass = MllpTcpServerConsumer.class, label = "mllp")
 public class MllpEndpoint extends DefaultEndpoint {
-    public static final char START_OF_BLOCK = 0x0b;      // VT (vertical tab)        - decimal 11, octal 013
-    public static final char END_OF_BLOCK = 0x1c;        // FS (file separator)      - decimal 28, octal 034
-    public static final char END_OF_DATA = 0x0d;         // CR (carriage return)     - decimal 13, octal 015
-    public static final int END_OF_STREAM = -1;          //
-    public static final char SEGMENT_DELIMITER = 0x0d;   // CR (carriage return)     - decimal 13, octal 015
-    public static final char MESSAGE_TERMINATOR = 0x0a;  // LF (line feed, new line) - decimal 10, octal 012
+  // Use constants from MllpProtocolConstants
+  @Deprecated()
+  public static final char START_OF_BLOCK = MllpProtocolConstants.START_OF_BLOCK;
+  @Deprecated()
+  public static final char END_OF_BLOCK = MllpProtocolConstants.END_OF_BLOCK;
+  @Deprecated()
+  public static final char END_OF_DATA = MllpProtocolConstants.END_OF_DATA;
+  @Deprecated()
+  public static final int END_OF_STREAM = MllpProtocolConstants.END_OF_STREAM;
+  @Deprecated()
+  public static final char SEGMENT_DELIMITER = MllpProtocolConstants.SEGMENT_DELIMITER;
+  @Deprecated()
+  public static final char MESSAGE_TERMINATOR = MllpProtocolConstants.MESSAGE_TERMINATOR;
+
+  @Deprecated // Use constants from MllpProtocolConstants
+  public static final Charset DEFAULT_CHARSET = MllpProtocolConstants.DEFAULT_CHARSET;
 
     private static final Logger LOG = LoggerFactory.getLogger(MllpEndpoint.class);
 
-    @UriPath @Metadata(required = "true")
+    @UriPath
+    @Metadata(required = "true")
     String hostname;
 
-    @UriPath @Metadata(required = "true")
+    @UriPath
+    @Metadata(required = "true")
     int port = -1;
 
-    @UriParam(label = "advanced", defaultValue = "5")
-    int backlog = 5;
-
-    @UriParam(label = "timeout", defaultValue = "30000")
-    int bindTimeout = 30000;
-
-    @UriParam(label = "timeout", defaultValue = "5000")
-    int bindRetryInterval = 5000;
-
-    @UriParam(label = "timeout", defaultValue = "60000")
-    int acceptTimeout = 60000;
-
-    @UriParam(label = "timeout", defaultValue = "30000")
-    int connectTimeout = 30000;
-
-    @UriParam(label = "timeout", defaultValue = "10000")
-    int receiveTimeout = 10000;
-
-    @UriParam(label = "timeout", defaultValue = "-1")
-    int maxReceiveTimeouts = -1;
-
-    @UriParam(label = "timeout", defaultValue = "500")
-    int readTimeout = 500;
-
-    @UriParam(defaultValue = "true")
-    boolean keepAlive = true;
-
-    @UriParam(defaultValue = "true")
-    boolean tcpNoDelay = true;
-
-    @UriParam
-    boolean reuseAddress;
-
     @UriParam(label = "advanced")
-    Integer receiveBufferSize;
-
-    @UriParam(label = "advanced")
-    Integer sendBufferSize;
-
-    @UriParam(defaultValue = "true")
-    boolean autoAck = true;
-
-    @UriParam(defaultValue = "true")
-    boolean hl7Headers = true;
+    MllpConfiguration configuration;
 
-    @UriParam(defaultValue = "true")
-    boolean bufferWrites = true;
+    Long lastConnectionActivityTicks;
+    Long lastConnectionEstablishedTicks;
+    Long lastConnectionTerminatedTicks;
 
-    @UriParam(defaultValue = "false")
-    boolean validatePayload;
-
-    @UriParam(label = "codec")
-    String charsetName;
-
-    public MllpEndpoint(String uri, MllpComponent component) {
+    public MllpEndpoint(String uri, MllpComponent component, MllpConfiguration configuration) {
         super(uri, component);
+        this.configuration = configuration.copy();
     }
 
     @Override
-    public ExchangePattern getExchangePattern() {
-        return ExchangePattern.InOut;
+    public Exchange createExchange(Exchange exchange) {
+        Exchange mllpExchange = super.createExchange(exchange);
+        setExchangeProperties(mllpExchange);
+        return mllpExchange;
     }
 
     @Override
@@ -126,15 +106,18 @@ public class MllpEndpoint extends DefaultEndpoint {
     }
 
     @Override
-    public Exchange createExchange(Exchange exchange) {
-        Exchange mllpExchange = super.createExchange(exchange);
-        setExchangeProperties(mllpExchange);
-        return mllpExchange;
+    public ExchangePattern getExchangePattern() {
+        return ExchangePattern.InOut;
+    }
+
+    @Override
+    public boolean isSynchronous() {
+        return true;
     }
 
     private void setExchangeProperties(Exchange mllpExchange) {
-        if (charsetName != null) {
-            mllpExchange.setProperty(Exchange.CHARSET_NAME, charsetName);
+        if (configuration.hasCharsetName()) {
+            mllpExchange.setProperty(Exchange.CHARSET_NAME, configuration.getCharsetName());
         }
     }
 
@@ -144,32 +127,49 @@ public class MllpEndpoint extends DefaultEndpoint {
     }
 
     public Consumer createConsumer(Processor processor) throws Exception {
-        LOG.trace("({}).createConsumer(processor)", this.getEndpointKey());
+        LOG.trace("({}).createConsumer(Processor)", this.getEndpointKey());
         Consumer consumer = new MllpTcpServerConsumer(this, processor);
         configureConsumer(consumer);
         return consumer;
     }
 
-    @Override
-    public boolean isSynchronous() {
+    public boolean isSingleton() {
         return true;
     }
 
-    public boolean isSingleton() {
-        return true;
+    @ManagedAttribute(description = "Last activity time")
+    public Date getLastConnectionActivityTime() {
+        if (lastConnectionActivityTicks != null) {
+            return new Date(lastConnectionActivityTicks);
+        }
+
+        return null;
+    }
+
+    @ManagedAttribute(description = "Last connection established time")
+    public Date getLastConnectionEstablishedTime() {
+        if (lastConnectionEstablishedTicks != null) {
+            return new Date(lastConnectionEstablishedTicks);
+        }
+
+        return null;
     }
 
-    public String getCharsetName() {
-        return charsetName;
+    @ManagedAttribute(description = "Last connection terminated time")
+    public Date getLastConnectionTerminatedTime() {
+        return lastConnectionTerminatedTicks != null ? new Date(lastConnectionTerminatedTicks) : null;
     }
 
-    /**
-     * Set the CamelCharsetName property on the exchange
-     *
-     * @param charsetName the charset
-     */
-    public void setCharsetName(String charsetName) {
-        this.charsetName = charsetName;
+    public void updateLastConnectionActivityTicks() {
+        lastConnectionActivityTicks = System.currentTimeMillis();
+    }
+
+    public void updateLastConnectionEstablishedTicks() {
+        lastConnectionEstablishedTicks = System.currentTimeMillis();
+    }
+
+    public void updateLastConnectionTerminatedTicks() {
+        lastConnectionTerminatedTicks = System.currentTimeMillis();
     }
 
     public String getHostname() {
@@ -200,235 +200,318 @@ public class MllpEndpoint extends DefaultEndpoint {
         this.port = port;
     }
 
-    public int getBacklog() {
-        return backlog;
+    public boolean hasConfiguration() {
+        return configuration != null;
     }
 
-    /**
-     * The maximum queue length for incoming connection indications (a request to connect) is set to the backlog parameter. If a connection indication arrives when the queue is full, the connection
-     * is refused.
-     */
-    public void setBacklog(int backlog) {
-        this.backlog = backlog;
+    public MllpConfiguration getConfiguration() {
+        return configuration;
     }
 
-    public int getBindTimeout() {
-        return bindTimeout;
+    public void setConfiguration(MllpConfiguration configuration) {
+        if (hasConfiguration()) {
+            this.configuration.copy(configuration);
+        } else {
+            this.configuration = configuration.copy();
+        }
     }
 
-    /**
-     * TCP Server Only - The number of milliseconds to retry binding to a server port
-     */
-    public void setBindTimeout(int bindTimeout) {
-        this.bindTimeout = bindTimeout;
-    }
+    public Charset determineCharset(byte[] hl7Bytes, String msh18) {
+        Charset answer = MllpProtocolConstants.DEFAULT_CHARSET;
+
+        if (configuration.hasCharsetName()) {
+            String charsetName = configuration.getCharsetName();
+            if (Charset.isSupported(charsetName)) {
+                answer = Charset.forName(charsetName);
+            } else {
+                LOG.warn("Unsupported Character Set {} configured for component - using default character set {}", charsetName, MllpProtocolConstants.DEFAULT_CHARSET);
+            }
+        } else if (msh18 != null && !msh18.isEmpty()) {
+            if (MllpProtocolConstants.MSH18_VALUES.containsKey(msh18)) {
+                answer = MllpProtocolConstants.MSH18_VALUES.get(msh18);
+            } else {
+                LOG.warn("Unsupported Character Set {} specified for MSH-18 - using default character set {}", msh18, MllpProtocolConstants.DEFAULT_CHARSET);
+            }
+        } else {
+            String foundMsh18 = findMsh18(hl7Bytes);
+            if (foundMsh18 != null && !foundMsh18.isEmpty()) {
+                if (MllpProtocolConstants.MSH18_VALUES.containsKey(foundMsh18)) {
+                    answer = MllpProtocolConstants.MSH18_VALUES.get(foundMsh18);
+                } else {
+                    LOG.warn("Unsupported Character Set {} found in MSH-18 - using default character set {}", foundMsh18, MllpProtocolConstants.DEFAULT_CHARSET);
+                }
+            } else {
+                LOG.debug("Character Set not specified and no Character Set found in MSH-18 - using default character set {}", MllpProtocolConstants.DEFAULT_CHARSET);
+            }
+        }
 
-    public int getBindRetryInterval() {
-        return bindRetryInterval;
+        return answer;
     }
 
-    /**
-     * TCP Server Only - The number of milliseconds to wait between bind attempts
-     */
-    public void setBindRetryInterval(int bindRetryInterval) {
-        this.bindRetryInterval = bindRetryInterval;
-    }
+    public String createNewString(byte[] hl7Bytes, String msh18) {
+        if (hl7Bytes == null) {
+            return null;
+        } else if (hl7Bytes.length == 0) {
+            return "";
+        }
 
-    public int getAcceptTimeout() {
-        return acceptTimeout;
-    }
+        Charset charset = determineCharset(hl7Bytes, msh18);
 
-    /**
-     * Timeout (in milliseconds) while waiting for a TCP connection
-     * <p/>
-     * TCP Server Only
-     *
-     * @param acceptTimeout timeout in milliseconds
-     */
-    public void setAcceptTimeout(int acceptTimeout) {
-        this.acceptTimeout = acceptTimeout;
-    }
+        LOG.debug("Creating new String using Charset {}", charset);
 
-    public int getConnectTimeout() {
-        return connectTimeout;
+        return new String(hl7Bytes, charset);
     }
 
-    /**
-     * Timeout (in milliseconds) for establishing for a TCP connection
-     * <p/>
-     * TCP Client only
-     *
-     * @param connectTimeout timeout in milliseconds
-     */
-    public void setConnectTimeout(int connectTimeout) {
-        this.connectTimeout = connectTimeout;
-    }
+    // TODO:  Move this to HL7Util
+    public String findMsh18(byte[] hl7Message) {
+        if (hl7Message == null || hl7Message.length == 0) {
+            return null;
+        }
 
-    public int getReceiveTimeout() {
-        return receiveTimeout;
-    }
+        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;
+            }
+        }
 
-    /**
-     * The SO_TIMEOUT value (in milliseconds) used when waiting for the start of an MLLP frame
-     *
-     * @param receiveTimeout timeout in milliseconds
-     */
-    public void setReceiveTimeout(int receiveTimeout) {
-        this.receiveTimeout = receiveTimeout;
+        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;
     }
 
-    public int getMaxReceiveTimeouts() {
-        return maxReceiveTimeouts;
+    // Pass-through configuration methods
+    public void setBacklog(Integer backlog) {
+        configuration.setBacklog(backlog);
     }
 
-    /**
-     * The maximum number of timeouts (specified by receiveTimeout) allowed before the TCP Connection will be reset.
-     *
-     * @param maxReceiveTimeouts maximum number of receiveTimeouts
-     */
-    public void setMaxReceiveTimeouts(int maxReceiveTimeouts) {
-        this.maxReceiveTimeouts = maxReceiveTimeouts;
+    public void setBindTimeout(int bindTimeout) {
+        configuration.setBindTimeout(bindTimeout);
     }
 
-    public int getReadTimeout() {
-        return readTimeout;
+    public void setBindRetryInterval(int bindRetryInterval) {
+        configuration.setBindRetryInterval(bindRetryInterval);
     }
 
-    /**
-     * The SO_TIMEOUT value (in milliseconds) used after the start of an MLLP frame has been received
-     *
-     * @param readTimeout timeout in milliseconds
-     */
-    public void setReadTimeout(int readTimeout) {
-        this.readTimeout = readTimeout;
+    public void setAcceptTimeout(int acceptTimeout) {
+        configuration.setAcceptTimeout(acceptTimeout);
     }
 
-    public boolean isKeepAlive() {
-        return keepAlive;
+    public void setConnectTimeout(int connectTimeout) {
+        configuration.setConnectTimeout(connectTimeout);
     }
 
-    /**
-     * Enable/disable the SO_KEEPALIVE socket option.
-     *
-     * @param keepAlive enable SO_KEEPALIVE when true; otherwise disable SO_KEEPALIVE
-     */
-    public void setKeepAlive(boolean keepAlive) {
-        this.keepAlive = keepAlive;
+    public void setReceiveTimeout(int receiveTimeout) {
+        configuration.setReceiveTimeout(receiveTimeout);
     }
 
-    public boolean isTcpNoDelay() {
-        return tcpNoDelay;
+    public void setIdleTimeout(Integer idleTimeout) {
+        configuration.setIdleTimeout(idleTimeout);
     }
 
-    /**
-     * Enable/disable the TCP_NODELAY socket option.
-     *
-     * @param tcpNoDelay enable TCP_NODELAY when true; otherwise disable TCP_NODELAY
-     */
-    public void setTcpNoDelay(boolean tcpNoDelay) {
-        this.tcpNoDelay = tcpNoDelay;
+    public void setReadTimeout(int readTimeout) {
+        configuration.setReadTimeout(readTimeout);
     }
 
-    public boolean isReuseAddress() {
-        return reuseAddress;
+    public void setKeepAlive(Boolean keepAlive) {
+        configuration.setKeepAlive(keepAlive);
     }
 
-    /**
-     * Enable/disable the SO_REUSEADDR socket option.
-     *
-     * @param reuseAddress enable SO_REUSEADDR when true; otherwise disable SO_REUSEADDR
-     */
-    public void setReuseAddress(boolean reuseAddress) {
-        this.reuseAddress = reuseAddress;
+    public void setTcpNoDelay(Boolean tcpNoDelay) {
+        configuration.setTcpNoDelay(tcpNoDelay);
     }
 
-    public int getReceiveBufferSize() {
-        return receiveBufferSize;
+    public void setReuseAddress(Boolean reuseAddress) {
+        configuration.setReuseAddress(reuseAddress);
     }
 
-    /**
-     * Sets the SO_RCVBUF option to the specified value (in bytes)
-     *
-     * @param receiveBufferSize the SO_RCVBUF option value.  If null, the system default is used
-     */
     public void setReceiveBufferSize(Integer receiveBufferSize) {
-        this.receiveBufferSize = receiveBufferSize;
+        configuration.setReceiveBufferSize(receiveBufferSize);
     }
 
-    public int getSendBufferSize() {
-        return sendBufferSize;
+    public void setSendBufferSize(Integer sendBufferSize) {
+        configuration.setSendBufferSize(sendBufferSize);
     }
 
-    /**
-     * Sets the SO_SNDBUF option to the specified value (in bytes)
-     *
-     * @param sendBufferSize the SO_SNDBUF option value.  If null, the system default is used
-     */
-    public void setSendBufferSize(Integer sendBufferSize) {
-        this.sendBufferSize = sendBufferSize;
+    public void setAutoAck(Boolean autoAck) {
+        configuration.setAutoAck(autoAck);
     }
 
-    public boolean isAutoAck() {
-        return autoAck;
+    public void setHl7Headers(Boolean hl7Headers) {
+        configuration.setHl7Headers(hl7Headers);
     }
 
     /**
-     * Enable/Disable the automatic generation of a MLLP Acknowledgement
+     * @deprecated this parameter will be ignored.
      *
-     * MLLP Consumers only
-     *
-     * @param autoAck enabled if true, otherwise disabled
+     * @param bufferWrites
      */
-    public void setAutoAck(boolean autoAck) {
-        this.autoAck = autoAck;
+    public void setBufferWrites(Boolean bufferWrites) {
+        configuration.setBufferWrites(bufferWrites);
     }
 
-    public boolean isHl7Headers() {
-        return hl7Headers;
+    public void setRequireEndOfData(Boolean requireEndOfData) {
+        configuration.setRequireEndOfData(requireEndOfData);
     }
 
-    /**
-     * Enable/Disable the automatic generation of message headers from the HL7 Message
-     *
-     * MLLP Consumers only
-     *
-     * @param hl7Headers enabled if true, otherwise disabled
-     */
-    public void setHl7Headers(boolean hl7Headers) {
-        this.hl7Headers = hl7Headers;
+    public void setStringPayload(Boolean stringPayload) {
+        configuration.setStringPayload(stringPayload);
     }
 
-    public boolean isValidatePayload() {
-        return validatePayload;
+    public void setValidatePayload(Boolean validatePayload) {
+        configuration.setValidatePayload(validatePayload);
     }
 
-    /**
-     * Enable/Disable the validation of HL7 Payloads
-     *
-     * If enabled, HL7 Payloads received from external systems will be validated (see Hl7Util.generateInvalidPayloadExceptionMessage for details on the validation).
-     * If and invalid payload is detected, a MllpInvalidMessageException (for consumers) or a MllpInvalidAcknowledgementException will be thrown.
-     *
-     * @param validatePayload enabled if true, otherwise disabled
-     */
-    public void setValidatePayload(boolean validatePayload) {
-        this.validatePayload = validatePayload;
+    public void setCharsetName(String charsetName) {
+        configuration.setCharsetName(charsetName);
     }
 
-    public boolean isBufferWrites() {
-        return bufferWrites;
+    // Utility methods for producers and consumers
+
+    public boolean checkBeforeSendProperties(Exchange exchange, Socket socket, Logger log) {
+        boolean answer = true;
+
+        if (exchange.getProperty(MllpConstants.MLLP_RESET_CONNECTION_BEFORE_SEND, boolean.class)) {
+            log.warn("Exchange property " + MllpConstants.MLLP_RESET_CONNECTION_BEFORE_SEND + " = "
+                + exchange.getProperty(MllpConstants.MLLP_RESET_CONNECTION_BEFORE_SEND) + " - resetting connection");
+            doConnectionClose(socket, true, log);
+            answer = false;
+        } else if (exchange.getProperty(MllpConstants.MLLP_CLOSE_CONNECTION_BEFORE_SEND, boolean.class)) {
+            log.warn("Exchange property " + MllpConstants.MLLP_CLOSE_CONNECTION_BEFORE_SEND + " = "
+                + exchange.getProperty(MllpConstants.MLLP_CLOSE_CONNECTION_BEFORE_SEND) + " - closing connection");
+            doConnectionClose(socket, false, log);
+            answer = false;
+        }
+
+        return answer;
     }
 
-    /**
-     * Enable/Disable the validation of HL7 Payloads
-     *
-     * If enabled, MLLP Payloads are buffered and written to the external system in a single write(byte[]) operation.
-     * If disabled, the MLLP payload will not be buffered, and three write operations will be used.  The first operation
-     * will write the MLLP start-of-block character {0x0b (ASCII VT)}, the second operation will write the HL7 payload, and the
-     * third operation will writh the MLLP end-of-block character and the MLLP end-of-data character {[0x1c, 0x0d] (ASCII [FS, CR])}.
-     *
-     * @param bufferWrites enabled if true, otherwise disabled
-     */
-    public void setBufferWrites(boolean bufferWrites) {
-        this.bufferWrites = bufferWrites;
+    public boolean checkAfterSendProperties(Exchange exchange, Socket socket, Logger log) {
+        boolean answer = true;
+
+        if (exchange.getProperty(MllpConstants.MLLP_RESET_CONNECTION_AFTER_SEND, boolean.class)) {
+            log.warn("Exchange property " + MllpConstants.MLLP_RESET_CONNECTION_AFTER_SEND + " = "
+                + exchange.getProperty(MllpConstants.MLLP_RESET_CONNECTION_AFTER_SEND) + " - resetting connection");
+            doConnectionClose(socket, true, log);
+            answer = false;
+        } else if (exchange.getProperty(MllpConstants.MLLP_CLOSE_CONNECTION_AFTER_SEND, boolean.class)) {
+            log.warn("Exchange property " + MllpConstants.MLLP_CLOSE_CONNECTION_AFTER_SEND + " = "
+                + exchange.getProperty(MllpConstants.MLLP_CLOSE_CONNECTION_AFTER_SEND) + " - closing connection");
+            doConnectionClose(socket, false, log);
+            answer = false;
+        }
+
+        return answer;
+    }
+
+    public void doConnectionClose(Socket socket, boolean reset, Logger log) {
+        String ignoringCallLogFormat = "Ignoring {} Connection request because - {}: localAddress={} remoteAddress={}";
+
+        if (socket == null) {
+            if (log != null) {
+                log.debug(ignoringCallLogFormat, reset ? "Reset" : "Close", "Socket is null", "null", "null");
+            }
+        } else {
+            SocketAddress localSocketAddress = socket.getLocalSocketAddress();
+            SocketAddress remoteSocketAddress = socket.getRemoteSocketAddress();
+            if (!socket.isConnected()) {
+                if (log != null) {
+                    log.debug(ignoringCallLogFormat, reset ? "Reset" : "Close", "Socket is not connected", localSocketAddress, remoteSocketAddress);
+                }
+            } else if (socket.isClosed()) {
+                if (log != null) {
+                    log.debug(ignoringCallLogFormat, reset ? "Reset" : "Close", "Socket is already closed", localSocketAddress, remoteSocketAddress);
+                }
+            } else {
+                final String ignoringExceptionStringFormat = "Ignoring %s encountered calling %s on Socket: localAddress=%s remoteAddress=%s";
+                if (!socket.isInputShutdown()) {
+                    if (log != null) {
+                        log.trace("Shutting down input on Socket: localAddress={} remoteAddress={}", localSocketAddress, remoteSocketAddress);
+                    }
+                    try {
+                        socket.shutdownInput();
+                    } catch (Exception ioEx) {
+                        if (log != null && log.isDebugEnabled()) {
+                            String logMessage = String.format(ignoringExceptionStringFormat, ioEx.getClass().getSimpleName(), "shutdownInput()", localSocketAddress, remoteSocketAddress);
+                            log.debug(logMessage, ioEx);
+                        }
+                    }
+                }
+
+                if (!socket.isOutputShutdown()) {
+                    if (log != null) {
+                        log.trace("Shutting down output on Socket: localAddress={} remoteAddress={}", localSocketAddress, remoteSocketAddress);
+                    }
+                    try {
+                        socket.shutdownOutput();
+                    } catch (IOException ioEx) {
+                        if (log != null && log.isDebugEnabled()) {
+                            String logMessage = String.format(ignoringExceptionStringFormat, ioEx.getClass().getSimpleName(), "shutdownOutput()", localSocketAddress, remoteSocketAddress);
+                            log.debug(logMessage, ioEx);
+                        }
+                    }
+                }
+
+                if (reset) {
+                    final boolean on = true;
+                    final int linger = 0;
+                    if (log != null) {
+                        log.trace("Setting SO_LINGER to {} on Socket: localAddress={} remoteAddress={}", localSocketAddress, remoteSocketAddress);
+                    }
+                    try {
+                        socket.setSoLinger(on, linger);
+                    } catch (IOException ioEx) {
+                        if (log.isDebugEnabled()) {
+                            String methodString = String.format("setSoLinger(%b, %d)", on, linger);
+                            String logMessage = String.format(ignoringExceptionStringFormat, ioEx.getClass().getSimpleName(), methodString, localSocketAddress, remoteSocketAddress);
+                            log.debug(logMessage, ioEx);
+                        }
+                    }
+                }
+
+                try {
+                    if (log != null) {
+                        log.trace("Resetting Socket: localAddress={} remoteAddress={}", localSocketAddress, remoteSocketAddress);
+                    }
+                    socket.close();
+                } catch (IOException ioEx) {
+                    if (log.isDebugEnabled()) {
+                        String warningMessage = String.format(ignoringExceptionStringFormat, ioEx.getClass().getSimpleName(), "close()", localSocketAddress, remoteSocketAddress);
+                        log.debug(warningMessage, ioEx);
+                    }
+                }
+            }
+        }
     }
 }
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpException.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpException.java
index ffa908f..615ee6c 100644
--- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpException.java
+++ b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpException.java
@@ -14,67 +14,93 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.camel.component.mllp;
 
+import org.apache.camel.component.mllp.internal.Hl7Util;
+
 /**
  * Base class for all MLLP Exceptions, and also used as a generic MLLP exception
  */
 public class MllpException extends Exception {
-    private final byte[] hl7Message;
-    private final byte[] hl7Acknowledgement;
+    final byte[] hl7MessageBytes;
+    final byte[] hl7AcknowledgementBytes;
 
+    // No-payload constructors
     public MllpException(String message) {
-        super(message);
-        this.hl7Message = null;
-        this.hl7Acknowledgement = null;
+        this(message, (byte[]) null, (byte[]) null, (Throwable) null);
     }
 
-    public MllpException(String message, byte[] hl7Message) {
-        super(message);
-        this.hl7Message = (hl7Message != null && hl7Message.length > 0) ? hl7Message : null;
-        this.hl7Acknowledgement = null;
+    public MllpException(String message, Throwable cause) {
+        this(message, (byte[]) null, (byte[]) null, cause);
     }
 
-    public MllpException(String message, byte[] hl7Message, byte[] hl7Acknowledgement) {
-        super(message);
-        this.hl7Message = (hl7Message != null && hl7Message.length > 0) ? hl7Message : null;
-        this.hl7Acknowledgement = (hl7Acknowledgement != null && hl7Acknowledgement.length > 0) ? hl7Acknowledgement : null;
+    // Message only payload constructors
+    public MllpException(String message, byte[] hl7MessageBytes) {
+        this(message, hl7MessageBytes, (byte[]) null, (Throwable) null);
     }
 
-    public MllpException(String message, Throwable cause) {
-        super(message, cause);
-        this.hl7Message = null;
-        this.hl7Acknowledgement = null;
+    public MllpException(String message, byte[] hl7MessageBytes, Throwable cause) {
+        this(message, hl7MessageBytes, (byte[]) null, cause);
     }
 
-    public MllpException(String message, byte[] hl7Message, Throwable cause) {
-        super(message, cause);
-        this.hl7Message = (hl7Message != null && hl7Message.length > 0) ? hl7Message : null;
-        this.hl7Acknowledgement = null;
+    // Message payload and Acknowledgement payload constructors
+    public MllpException(String message, byte[] hl7MessageBytes, byte[] hl7AcknowledgementBytes) {
+        this(message, hl7MessageBytes, hl7AcknowledgementBytes, (Throwable) null);
     }
 
-    public MllpException(String message, byte[] hl7Message, byte[] hl7Acknowledgement, Throwable cause) {
+    public MllpException(String message, byte[] hl7MessageBytes, byte[] hl7AcknowledgementBytes, Throwable cause) {
         super(message, cause);
-        this.hl7Message = (hl7Message != null && hl7Message.length > 0) ? hl7Message : null;
-        this.hl7Acknowledgement = (hl7Acknowledgement != null && hl7Acknowledgement.length > 0) ? hl7Acknowledgement : null;
+
+        if (hl7MessageBytes != null && hl7MessageBytes.length > 0) {
+            this.hl7MessageBytes = hl7MessageBytes;
+        } else {
+            this.hl7MessageBytes = null;
+        }
+
+        if (hl7AcknowledgementBytes != null && hl7AcknowledgementBytes.length > 0) {
+            this.hl7AcknowledgementBytes = hl7AcknowledgementBytes;
+        } else {
+            this.hl7AcknowledgementBytes = null;
+        }
+    }
+
+
+    /**
+     * Determine if there is an HL7 message payload associated with this exception.
+     *
+     * @return true if this exception contains an HL7 message payload; false otherwise
+     */
+    public boolean hasHl7MessageBytes() {
+        return hl7MessageBytes != null && hl7MessageBytes.length > 0;
     }
 
+
     /**
      * Get the HL7 message payload associated with this exception, if any.
      *
-     * @return HL7 message payload
+     * @return the HL7 message payload; null if a message payload is not associated with this exception
      */
-    public byte[] getHl7Message() {
-        return hl7Message;
+    public byte[] getHl7MessageBytes() {
+        return hl7MessageBytes;
+    }
+
+    /**
+     * Determine if there is an HL7 message payload associated with this exception.
+     *
+     * @return true if this exception contains an HL7 message payload; false otherwise
+     */
+    public boolean hasHl7AcknowledgementBytes() {
+        return hl7AcknowledgementBytes != null && hl7AcknowledgementBytes.length > 0;
     }
 
     /**
      * Get the HL7 acknowledgement payload associated with this exception, if any.
      *
-     * @return HL7 acknowledgement payload
+     * @return the HL7 acknowledgement payload; null if an acknowledgement payload is not associated with this exception
      */
-    public byte[] getHl7Acknowledgement() {
-        return hl7Acknowledgement;
+    public byte[] getHl7AcknowledgementBytes() {
+        return hl7AcknowledgementBytes;
     }
 
     /**
@@ -84,23 +110,44 @@ public class MllpException extends Exception {
      */
     @Override
     public String getMessage() {
-        if (MllpComponent.isLogPhi()) {
-            return String.format("%s \n\t{hl7Message= %s} \n\t{hl7Acknowledgement= %s}",
-                    super.getMessage(), MllpComponent.covertBytesToPrintFriendlyString(hl7Message), MllpComponent.covertBytesToPrintFriendlyString(hl7Acknowledgement));
+        String answer;
+
+        if (hasHl7MessageBytes() || hasHl7AcknowledgementBytes()) {
+            String parentMessage = super.getMessage();
+
+            StringBuilder messageBuilder = new StringBuilder(parentMessage.length()
+                + (hasHl7MessageBytes() ? hl7MessageBytes.length : 0)
+                + (hasHl7AcknowledgementBytes() ? hl7AcknowledgementBytes.length : 0)
+            );
+
+            messageBuilder.append(parentMessage);
+
+            if (hasHl7MessageBytes()) {
+                messageBuilder.append("\n\t{hl7Message [")
+                    .append(hl7MessageBytes.length)
+                    .append("] = ");
+
+                Hl7Util.appendBytesAsPrintFriendlyString(messageBuilder, hl7MessageBytes, 0, hl7MessageBytes.length);
+
+                messageBuilder.append('}');
+            }
+
+            if (hasHl7AcknowledgementBytes()) {
+                messageBuilder.append("\n\t{hl7Acknowledgement [")
+                    .append(hl7AcknowledgementBytes.length)
+                    .append("] = ");
+
+                Hl7Util.appendBytesAsPrintFriendlyString(messageBuilder, hl7AcknowledgementBytes, 0, hl7AcknowledgementBytes.length);
+
+                messageBuilder.append('}');
+            }
+
+            answer = messageBuilder.toString();
         } else {
-            return super.getMessage();
+            answer = super.getMessage();
         }
-    }
 
-    /**
-     * Return the MLLP Payload that is most likely the cause of the Exception
-     *
-     * If the HL7 Acknowledgement is present, return it.  Otherwise, return the HL7 Message.
-     *
-     * @return the MLLP Payload with the framing error
-     */
-    public byte[] getMllpPayload() {
-        return (hl7Acknowledgement != null  &&  hl7Acknowledgement.length > 0) ? hl7Acknowledgement : hl7Message;
+        return answer;
     }
 
 }
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpFrameException.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpFrameException.java
deleted file mode 100644
index 7ae2dd1..0000000
--- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpFrameException.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * 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;
-
-/**
- * @deprecated - replaced by more specific exceptions.  MllpTimeoutException, MllpInvalidMessageException and MllpInvalidAcknowledgementException
- * Raised when a MLLP Producer or consumer encounters a corrupt MLLP Frame while attempting
- * to readEnvelopedPayload or writeEnvelopedMessage a MLLP payload.
- */
-@Deprecated
-public class MllpFrameException extends MllpException {
-
-    public MllpFrameException(String message, byte[] hl7Message) {
-        super(message, hl7Message);
-    }
-
-    public MllpFrameException(String message, byte[] hl7Message, byte[] hl7Acknowledgement) {
-        super(message, hl7Message, hl7Acknowledgement);
-    }
-
-    public MllpFrameException(String message, byte[] hl7Message, Throwable cause) {
-        super(message, hl7Message, cause);
-    }
-
-    public MllpFrameException(String message, byte[] hl7Message, byte[] hl7Acknowledgement, Throwable cause) {
-        super(message, hl7Message, hl7Acknowledgement, cause);
-    }
-
-}
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpInvalidAcknowledgementException.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpInvalidAcknowledgementException.java
index 2543bec..e265768 100644
--- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpInvalidAcknowledgementException.java
+++ b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpInvalidAcknowledgementException.java
@@ -14,13 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.camel.component.mllp;
 
 /**
  * Raised when a MLLP Producer receives a HL7 Acknowledgement for which the HL7 Acknowledgement type cannot be determined.
  */
 public class MllpInvalidAcknowledgementException extends MllpAcknowledgementException {
-
     public MllpInvalidAcknowledgementException(String message, byte[] hl7Message, byte[] hl7Acknowledgement) {
         super(message, hl7Message, hl7Acknowledgement);
     }
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpInvalidMessageException.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpInvalidMessageException.java
index 847a4de..d0ded5e 100644
--- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpInvalidMessageException.java
+++ b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpInvalidMessageException.java
@@ -14,13 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.camel.component.mllp;
 
 /**
  * Raised when a MLLP Consumer receives an invalid HL7 Message.
  */
 public class MllpInvalidMessageException extends MllpException {
-
     public MllpInvalidMessageException(String message, byte[] hl7Message) {
         super(message, hl7Message);
     }
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpNegativeAcknowledgementException.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpNegativeAcknowledgementException.java
index b57aebe..b4c91c1 100644
--- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpNegativeAcknowledgementException.java
+++ b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpNegativeAcknowledgementException.java
@@ -14,13 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.camel.component.mllp;
 
 /**
  * Abstract base for all MLLP Negative Acknowledgements
  */
 public abstract class MllpNegativeAcknowledgementException extends MllpAcknowledgementException {
-
     public MllpNegativeAcknowledgementException(String message, byte[] hl7Message, byte[] hl7Acknowledgement) {
         super(message, hl7Message, hl7Acknowledgement);
     }
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpProtocolConstants.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpProtocolConstants.java
new file mode 100644
index 0000000..8529637
--- /dev/null
+++ b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpProtocolConstants.java
@@ -0,0 +1,73 @@
+/**
+ * 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.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Constants for the MLLP Protocol and the Camel MLLP component.
+ */
+public final class MllpProtocolConstants {
+    public static final char START_OF_BLOCK = 0x0b;      // VT (vertical tab)        - decimal 11, octal 013
+    public static final char END_OF_BLOCK = 0x1c;        // FS (file separator)      - decimal 28, octal 034
+    public static final char END_OF_DATA = 0x0d;         // CR (carriage return)     - decimal 13, octal 015
+    public static final int END_OF_STREAM = -1;          //
+    public static final char SEGMENT_DELIMITER = 0x0d;   // CR (carriage return)     - decimal 13, octal 015
+    public static final char MESSAGE_TERMINATOR = 0x0a;  // LF (line feed, new line) - decimal 10, octal 012
+
+    public static final byte[] PAYLOAD_TERMINATOR;
+
+    public static final Charset DEFAULT_CHARSET = StandardCharsets.US_ASCII;
+    public static final Map<String, Charset> MSH18_VALUES;
+
+    static {
+        PAYLOAD_TERMINATOR = new byte[2];
+        PAYLOAD_TERMINATOR[0] = MllpProtocolConstants.END_OF_BLOCK;
+        PAYLOAD_TERMINATOR[1] = MllpProtocolConstants.END_OF_DATA;
+
+        MSH18_VALUES = new HashMap<>(15);
+        MSH18_VALUES.put("ASCII", StandardCharsets.US_ASCII);
+        MSH18_VALUES.put("8859/1", StandardCharsets.ISO_8859_1);
+
+        MSH18_VALUES.put("8859/2", Charset.forName("ISO-8859-2"));
+        MSH18_VALUES.put("8859/3", Charset.forName("ISO-8859-3"));
+        MSH18_VALUES.put("8859/4", Charset.forName("ISO-8859-4"));
+        MSH18_VALUES.put("8859/5", Charset.forName("ISO-8859-5"));
+        MSH18_VALUES.put("8859/6", Charset.forName("ISO-8859-6"));
+        MSH18_VALUES.put("8859/7", Charset.forName("ISO-8859-7"));
+        MSH18_VALUES.put("8859/8", Charset.forName("ISO-8859-8"));
+        MSH18_VALUES.put("8859/9", Charset.forName("ISO-8859-9"));
+
+    /*
+      // These are defined in the HL7 Spec, but I don't know how to map them to Java charset names
+
+      MSH18_VALUES.put("JAS2020", "????");
+      MSH18_VALUES.put("JIS X 0202", "????");
+      MSH18_VALUES.put("JIS X 0201-1976", "????");
+      MSH18_VALUES.put("JIS X 0208-1990", "????");
+      MSH18_VALUES.put("JIS X 0212-1990", "????");
+    */
+    }
+
+    private MllpProtocolConstants() {
+        //utility class, never constructed
+    }
+}
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpReceiveException.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpReceiveException.java
index bf8826b..c70a82f 100644
--- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpReceiveException.java
+++ b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpReceiveException.java
@@ -14,13 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.camel.component.mllp;
 
 /**
  * Raised when a MLLP Consumer cannot deliver the MLLP Acknowledgement
  */
 public class MllpReceiveException extends MllpException {
-
     public MllpReceiveException(String message) {
         super(message);
     }
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpInvalidMessageException.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpSocketException.java
similarity index 69%
copy from components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpInvalidMessageException.java
copy to components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpSocketException.java
index 847a4de..d9cab79 100644
--- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpInvalidMessageException.java
+++ b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpSocketException.java
@@ -14,18 +14,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.component.mllp;
 
-/**
- * Raised when a MLLP Consumer receives an invalid HL7 Message.
- */
-public class MllpInvalidMessageException extends MllpException {
+package org.apache.camel.component.mllp;
 
-    public MllpInvalidMessageException(String message, byte[] hl7Message) {
-        super(message, hl7Message);
+public class MllpSocketException extends Exception {
+    public MllpSocketException(String message) {
+        super(message);
     }
 
-    public MllpInvalidMessageException(String message, byte[] hl7Message, Throwable cause) {
-        super(message, hl7Message, cause);
+    public MllpSocketException(String message, Throwable cause) {
+        super(message, cause);
     }
 }
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 621e5c0..6c05ae4 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
@@ -14,164 +14,271 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.camel.component.mllp;
 
 import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.net.Socket;
+import java.net.SocketAddress;
 import java.net.SocketException;
-
+import java.net.SocketTimeoutException;
+import java.nio.charset.Charset;
+import java.util.Date;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.camel.Endpoint;
 import org.apache.camel.Exchange;
 import org.apache.camel.Message;
+import org.apache.camel.api.management.ManagedAttribute;
 import org.apache.camel.api.management.ManagedOperation;
 import org.apache.camel.api.management.ManagedResource;
-import org.apache.camel.component.mllp.impl.Hl7Util;
-import org.apache.camel.component.mllp.impl.MllpBufferedSocketWriter;
-import org.apache.camel.component.mllp.impl.MllpSocketReader;
-import org.apache.camel.component.mllp.impl.MllpSocketUtil;
-import org.apache.camel.component.mllp.impl.MllpSocketWriter;
+import org.apache.camel.component.mllp.internal.Hl7Util;
+import org.apache.camel.component.mllp.internal.MllpSocketBuffer;
 import org.apache.camel.impl.DefaultProducer;
-import org.apache.camel.util.IOHelper;
-
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_ACKNOWLEDGEMENT;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_ACKNOWLEDGEMENT_TYPE;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_CLOSE_CONNECTION_AFTER_SEND;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_CLOSE_CONNECTION_BEFORE_SEND;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_LOCAL_ADDRESS;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_REMOTE_ADDRESS;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_RESET_CONNECTION_AFTER_SEND;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_RESET_CONNECTION_BEFORE_SEND;
-import static org.apache.camel.component.mllp.MllpEndpoint.SEGMENT_DELIMITER;
 
 /**
  * The MLLP producer.
  */
-@ManagedResource(description = "MllpTcpClient Producer")
-public class MllpTcpClientProducer extends DefaultProducer {
-    MllpEndpoint endpoint;
-
+@ManagedResource(description = "MLLP Producer")
+public class MllpTcpClientProducer extends DefaultProducer implements Runnable {
     Socket socket;
 
-    MllpSocketReader mllpSocketReader;
-    MllpSocketWriter mllpSocketWriter;
+    final MllpSocketBuffer mllpBuffer;
+
+    ScheduledExecutorService idleTimeoutExecutor;
+    long lastProcessCallTicks = -1;
+
+    private String cachedLocalAddress;
+    private String cachedRemoteAddress;
+    private String cachedCombinedAddress;
 
     public MllpTcpClientProducer(MllpEndpoint endpoint) throws SocketException {
         super(endpoint);
-        log.trace("MllpTcpClientProducer(endpoint)");
+        log.trace("Constructing MllpTcpClientProducer for endpoint URI {}", endpoint.getEndpointUri());
+
+        mllpBuffer = new MllpSocketBuffer(endpoint);
+    }
+
+    @ManagedAttribute(description = "Last activity time")
+    public Date getLastActivityTime() {
+        return new Date(lastProcessCallTicks);
+    }
+
+    @ManagedAttribute(description = "Connection")
+    public String getConnectionAddress() {
+        if (cachedCombinedAddress != null) {
+            return cachedCombinedAddress;
+        }
+
+        return MllpSocketBuffer.formatAddressString(null, null);
+    }
+
+    @ManagedOperation(description = "Close Connection")
+    public void closeConnection() {
+        log.info("Close Connection for address {} called via JMX", getConnectionAddress());
 
-        this.endpoint = endpoint;
+        mllpBuffer.closeSocket(socket);
+    }
+
+    @ManagedOperation(description = "Reset Connection")
+    public void resetConnection() {
+        log.info("Reset Connection for address {} requested via JMX", getConnectionAddress());
+
+        mllpBuffer.resetSocket(socket);
     }
 
     @Override
     protected void doStart() throws Exception {
-        log.trace("doStart()");
+        if (getConfiguration().hasIdleTimeout()) {
+            // Get the URI without options
+            String fullEndpointKey = getEndpoint().getEndpointKey();
+            String endpointKey;
+            if (fullEndpointKey.contains("?")) {
+                endpointKey = fullEndpointKey.substring(0, fullEndpointKey.indexOf('?'));
+            } else {
+                endpointKey = fullEndpointKey;
+            }
+
+            idleTimeoutExecutor = Executors.newSingleThreadScheduledExecutor(new IdleTimeoutThreadFactory(endpointKey));
+        }
 
         super.doStart();
     }
 
     @Override
     protected void doStop() throws Exception {
-        log.trace("doStop()");
+        if (idleTimeoutExecutor != null) {
+            idleTimeoutExecutor.shutdown();
+            idleTimeoutExecutor = null;
+        }
 
-        MllpSocketUtil.close(socket, log, "Stopping component");
+        mllpBuffer.resetSocket(socket);
 
         super.doStop();
     }
 
-    @ManagedOperation(description = "Close client socket")
-    public void closeMllpSocket() {
-        MllpSocketUtil.close(socket, log, "JMX triggered closing socket");
-    }
+    @Override
+    public synchronized void process(Exchange exchange) throws Exception {
+        log.trace("Processing Exchange {}", exchange.getExchangeId());
 
-    @ManagedOperation(description = "Reset client socket")
-    public void resetMllpSocket() {
-        MllpSocketUtil.reset(socket, log, "JMX triggered resetting socket");
-    }
+        Message message = exchange.hasOut() ? exchange.getOut() : exchange.getIn();
 
-    @Override
-    public void process(Exchange exchange) throws Exception {
-        log.trace("process(exchange)");
-
-        // Check BEFORE_SEND Properties
-        if (exchange.getProperty(MLLP_RESET_CONNECTION_BEFORE_SEND, boolean.class)) {
-            MllpSocketUtil.reset(socket, log, "Exchange property " + MLLP_RESET_CONNECTION_BEFORE_SEND + " = " + exchange.getProperty(MLLP_RESET_CONNECTION_BEFORE_SEND, boolean.class));
-            return;
-        } else if (exchange.getProperty(MLLP_CLOSE_CONNECTION_BEFORE_SEND, boolean.class)) {
-            MllpSocketUtil.close(socket, log, "Exchange property " + MLLP_CLOSE_CONNECTION_BEFORE_SEND + " = " + exchange.getProperty(MLLP_CLOSE_CONNECTION_BEFORE_SEND, boolean.class));
-            return;
-        }
+        getEndpoint().checkBeforeSendProperties(exchange, socket, log);
 
         // Establish a connection if needed
         try {
             checkConnection();
-        } catch (IOException ioEx) {
-            exchange.setException(ioEx);
-            return;
-        }
 
-        Message message;
-        if (exchange.hasOut()) {
-            message = exchange.getOut();
-        } else {
-            message = exchange.getIn();
-        }
+            if (cachedLocalAddress != null) {
+                message.setHeader(MllpConstants.MLLP_LOCAL_ADDRESS, cachedLocalAddress);
+            }
 
-        message.setHeader(MLLP_LOCAL_ADDRESS, socket.getLocalAddress().toString());
-        message.setHeader(MLLP_REMOTE_ADDRESS, socket.getRemoteSocketAddress().toString());
+            if (cachedRemoteAddress != null) {
+                message.setHeader(MllpConstants.MLLP_REMOTE_ADDRESS, cachedRemoteAddress);
+            }
 
-        // Send the message to the external system
-        byte[] hl7MessageBytes = message.getMandatoryBody(byte[].class);
-        byte[] acknowledgementBytes = null;
+            // Send the message to the external system
+            byte[] hl7MessageBytes = null;
+            Object messageBody = message.getBody();
+            if (messageBody == null) {
+                exchange.setException(new MllpInvalidMessageException("message body is null", hl7MessageBytes));
+                return;
+            } else if (messageBody instanceof byte[]) {
+                hl7MessageBytes = (byte[]) messageBody;
+            } else if (messageBody instanceof String) {
+                String stringBody = (String) messageBody;
+                byte[] tmpHl7MessageBytes = stringBody.getBytes(MllpProtocolConstants.DEFAULT_CHARSET);
+                Charset tmpCharset = getEndpoint().determineCharset(tmpHl7MessageBytes, null);
+                exchange.setProperty(Exchange.CHARSET_NAME, tmpCharset.name());
+                if (tmpCharset != null && tmpCharset != MllpProtocolConstants.DEFAULT_CHARSET) {
+                    hl7MessageBytes = stringBody.getBytes(tmpCharset);
+                    exchange.setProperty(Exchange.CHARSET_NAME, tmpCharset.name());
+                } else {
+                    hl7MessageBytes = tmpHl7MessageBytes;
+                    exchange.setProperty(Exchange.CHARSET_NAME, MllpProtocolConstants.DEFAULT_CHARSET.name());
+                }
+            }
 
-        try {
             log.debug("Sending message to external system");
-            mllpSocketWriter.writeEnvelopedPayload(hl7MessageBytes, null);
-            log.debug("Reading acknowledgement from external system");
-            acknowledgementBytes = mllpSocketReader.readEnvelopedPayload(hl7MessageBytes);
-        } catch (MllpWriteException writeEx) {
-            MllpSocketUtil.reset(socket, log, writeEx.getMessage());
-            exchange.setException(writeEx);
-            return;
-        } catch (MllpReceiveException ackReceiveEx) {
-            MllpSocketUtil.reset(socket, log, ackReceiveEx.getMessage());
-            exchange.setException(ackReceiveEx);
-            return;
-        } catch (MllpException mllpEx) {
-            Throwable mllpExCause = mllpEx.getCause();
-            if (mllpExCause instanceof IOException) {
-                MllpSocketUtil.reset(socket, log, mllpEx.getMessage());
+            getEndpoint().updateLastConnectionEstablishedTicks();
+
+            try {
+                mllpBuffer.setEnvelopedMessage(hl7MessageBytes);
+                mllpBuffer.writeTo(socket);
+            } catch (MllpSocketException writeEx) {
+                // Connection may have been reset - try one more time
+                log.debug("Exception encountered reading acknowledgement - attempting reconnect", writeEx);
+                try {
+                    checkConnection();
+                    log.trace("Reconnected succeeded - resending payload");
+                    try {
+                        mllpBuffer.writeTo(socket);
+                    } catch (MllpSocketException retryWriteEx) {
+                        exchange.setException(retryWriteEx);
+                    }
+                } catch (IOException reconnectEx) {
+                    log.debug("Reconnected failed - sending exception to exchange", reconnectEx);
+                    exchange.setException(reconnectEx);
+                }
+
             }
-            exchange.setException(mllpEx);
-            return;
-        }
 
-        log.debug("Populating message headers with the acknowledgement from the external system");
-        message.setHeader(MLLP_ACKNOWLEDGEMENT, acknowledgementBytes);
-        message.setHeader(MLLP_ACKNOWLEDGEMENT_STRING, new String(acknowledgementBytes, IOHelper.getCharsetName(exchange, true)));
+            if (exchange.getException() == null) {
+                log.debug("Reading acknowledgement from external system");
+                try {
+                    mllpBuffer.reset();
+                    mllpBuffer.readFrom(socket);
+                } catch (MllpSocketException receiveAckEx) {
+                    // Connection may have been reset - try one more time
+                    log.debug("Exception encountered reading acknowledgement - attempting reconnect", receiveAckEx);
+                    try {
+                        checkConnection();
+                    } catch (IOException reconnectEx) {
+                        log.debug("Reconnected failed - sending original exception to exchange", reconnectEx);
+                        exchange.setException(new MllpAcknowledgementReceiveException("Exception encountered reading acknowledgement", hl7MessageBytes, receiveAckEx));
+                    }
 
-        if (endpoint.validatePayload) {
-            String exceptionMessage = Hl7Util.generateInvalidPayloadExceptionMessage(acknowledgementBytes);
-            if (exceptionMessage != null) {
-                exchange.setException(new MllpInvalidAcknowledgementException(exceptionMessage, hl7MessageBytes, acknowledgementBytes));
-                return;
-            }
-        }
+                    if (exchange.getException() == null) {
+                        log.trace("Reconnected succeeded - resending payload");
+                        try {
+                            mllpBuffer.setEnvelopedMessage(hl7MessageBytes);
+                            mllpBuffer.writeTo(socket);
+                        } catch (MllpSocketException writeRetryEx) {
+                            exchange.setException(new MllpWriteException("Failed to write HL7 message to socket", hl7MessageBytes, writeRetryEx));
+                        }
 
-        log.debug("Processing the acknowledgement from the external system");
-        try {
-            String acknowledgementType = processAcknowledgment(hl7MessageBytes, acknowledgementBytes);
-            message.setHeader(MLLP_ACKNOWLEDGEMENT_TYPE, acknowledgementType);
-        } catch (MllpException mllpEx) {
-            exchange.setException(mllpEx);
-            return;
-        }
+                        if (exchange.getException() == null) {
+                            log.trace("Resend succeeded - reading acknowledgement");
+                            try {
+                                mllpBuffer.reset();
+                                mllpBuffer.readFrom(socket);
+                            } catch (MllpSocketException secondReceiveEx) {
+                                if (mllpBuffer.isEmpty()) {
+                                    Exception exchangeEx = new MllpAcknowledgementReceiveException("Exception encountered receiving Acknowledgement", hl7MessageBytes, secondReceiveEx);
+                                    exchange.setException(exchangeEx);
+                                } else {
+                                    byte[] partialAcknowledgment = mllpBuffer.toByteArray();
+                                    mllpBuffer.reset();
+                                    Exception exchangeEx = new MllpAcknowledgementReceiveException("Exception encountered receiving complete Acknowledgement",
+                                        hl7MessageBytes, partialAcknowledgment, secondReceiveEx);
+                                    exchange.setException(exchangeEx);
+                                }
+                            }
+                        }
+                    }
+                } catch (SocketTimeoutException timeoutEx) {
+                    if (mllpBuffer.isEmpty()) {
+                        exchange.setException(new MllpAcknowledgementTimeoutException("Timeout receiving HL7 Acknowledgement", hl7MessageBytes, timeoutEx));
+                    } else {
+                        exchange.setException(new MllpAcknowledgementTimeoutException("Timeout receiving complete HL7 Acknowledgement", hl7MessageBytes, mllpBuffer.toByteArray(), timeoutEx));
+                        mllpBuffer.reset();
+                    }
+                }
+
+                if (exchange.getException() == null) {
+                    if (mllpBuffer.hasCompleteEnvelope()) {
+                        byte[] acknowledgementBytes = mllpBuffer.toMllpPayload();
+
+                        log.debug("Populating message headers with the acknowledgement from the external system");
+                        message.setHeader(MllpConstants.MLLP_ACKNOWLEDGEMENT, acknowledgementBytes);
+                        if (acknowledgementBytes != null && acknowledgementBytes.length > 0) {
+                            message.setHeader(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING, getEndpoint().createNewString(acknowledgementBytes, message.getHeader(MllpConstants.MLLP_CHARSET, String.class)));
+                        } else {
+                            message.setHeader(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING, "");
+                        }
+
+                        if (getConfiguration().isValidatePayload()) {
+                            String exceptionMessage = Hl7Util.generateInvalidPayloadExceptionMessage(acknowledgementBytes);
+                            if (exceptionMessage != null) {
+                                exchange.setException(new MllpInvalidAcknowledgementException(exceptionMessage, hl7MessageBytes, acknowledgementBytes));
+                            }
+                        }
+
+                        if (exchange.getException() == null) {
+                            log.debug("Processing the acknowledgement from the external system");
+                            try {
+                                message.setHeader(MllpConstants.MLLP_ACKNOWLEDGEMENT_TYPE, processAcknowledgment(hl7MessageBytes, acknowledgementBytes));
+                            } catch (MllpNegativeAcknowledgementException nackEx) {
+                                message.setHeader(MllpConstants.MLLP_ACKNOWLEDGEMENT_TYPE, nackEx.getAcknowledgmentType());
+                                exchange.setException(nackEx);
+                            }
+
+                            getEndpoint().checkAfterSendProperties(exchange, socket, log);
+                        }
+                    } else {
+                        exchange.setException(new MllpInvalidAcknowledgementException("Invalid acknowledgement received", hl7MessageBytes, mllpBuffer.toByteArray()));
+                    }
+                }
+            }
 
-        // Check AFTER_SEND Properties
-        if (exchange.getProperty(MLLP_RESET_CONNECTION_AFTER_SEND, boolean.class)) {
-            MllpSocketUtil.reset(socket, log, "Exchange property " + MLLP_RESET_CONNECTION_AFTER_SEND + " = " + exchange.getProperty(MLLP_RESET_CONNECTION_AFTER_SEND, boolean.class));
-        } else if (exchange.getProperty(MLLP_CLOSE_CONNECTION_AFTER_SEND, boolean.class)) {
-            MllpSocketUtil.close(socket, log, "Exchange property " + MLLP_CLOSE_CONNECTION_AFTER_SEND + " = " + exchange.getProperty(MLLP_CLOSE_CONNECTION_AFTER_SEND, boolean.class));
+        } catch (IOException ioEx) {
+            exchange.setException(ioEx);
+        } finally {
+            mllpBuffer.reset();
         }
     }
 
@@ -184,7 +291,7 @@ public class MllpTcpClientProducer extends DefaultProducer {
             // First, find the beginning of the MSA segment - should be the second segment
             int msaStartIndex = -1;
             for (int i = 0; i < hl7AcknowledgementBytes.length; ++i) {
-                if (SEGMENT_DELIMITER == hl7AcknowledgementBytes[i]) {
+                if (MllpProtocolConstants.SEGMENT_DELIMITER == hl7AcknowledgementBytes[i]) {
                     final byte bM = 77;
                     final byte bS = 83;
                     final byte bC = 67;
@@ -238,7 +345,7 @@ public class MllpTcpClientProducer extends DefaultProducer {
                 }
 
             }
-            if (-1 == msaStartIndex  &&  endpoint.validatePayload) {
+            if (-1 == msaStartIndex && getConfiguration().isValidatePayload()) {
                 // Didn't find an MSA
                 throw new MllpInvalidAcknowledgementException("MSA Not found in acknowledgement", hl7MessageBytes, hl7AcknowledgementBytes);
             }
@@ -248,62 +355,116 @@ public class MllpTcpClientProducer extends DefaultProducer {
     }
 
     /**
-     * Validate the TCP Connection, if closed opens up the socket with
-     * the value set via endpoint configuration
+     * Validate the TCP Connection
      *
-     * @throws IOException if the connection is not valid, otherwise the Exception is not
-     *                     encountered while checking the connection
+     * @return null if the connection is valid, otherwise the Exception encounted checking the connection
      */
-    private void checkConnection() throws IOException {
+    void checkConnection() throws IOException {
         if (null == socket || socket.isClosed() || !socket.isConnected()) {
             socket = new Socket();
 
-            socket.setKeepAlive(endpoint.keepAlive);
-            socket.setTcpNoDelay(endpoint.tcpNoDelay);
-            if (null != endpoint.receiveBufferSize) {
-                socket.setReceiveBufferSize(endpoint.receiveBufferSize);
-            } else {
-                endpoint.receiveBufferSize = socket.getReceiveBufferSize();
+            if (getConfiguration().hasKeepAlive()) {
+                socket.setKeepAlive(getConfiguration().getKeepAlive());
             }
-            if (null != endpoint.sendBufferSize) {
-                socket.setSendBufferSize(endpoint.sendBufferSize);
-            } else {
-                endpoint.sendBufferSize = socket.getSendBufferSize();
+            if (getConfiguration().hasTcpNoDelay()) {
+                socket.setTcpNoDelay(getConfiguration().getTcpNoDelay());
             }
-            socket.setReuseAddress(endpoint.reuseAddress);
+
+            if (getConfiguration().hasReceiveBufferSize()) {
+                socket.setReceiveBufferSize(getConfiguration().getReceiveBufferSize());
+            }
+            if (getConfiguration().hasSendBufferSize()) {
+                socket.setSendBufferSize(getConfiguration().getSendBufferSize());
+            }
+            if (getConfiguration().hasReuseAddress()) {
+                socket.setReuseAddress(getConfiguration().getReuseAddress());
+            }
+
             socket.setSoLinger(false, -1);
 
             InetSocketAddress socketAddress;
-            if (null == endpoint.getHostname()) {
-                socketAddress = new InetSocketAddress(endpoint.getPort());
+            if (null == getEndpoint().getHostname()) {
+                socketAddress = new InetSocketAddress(getEndpoint().getPort());
             } else {
-                socketAddress = new InetSocketAddress(endpoint.getHostname(), endpoint.getPort());
+                socketAddress = new InetSocketAddress(getEndpoint().getHostname(), getEndpoint().getPort());
+            }
+
+            socket.connect(socketAddress, getConfiguration().getConnectTimeout());
+            SocketAddress localSocketAddress = socket.getLocalSocketAddress();
+            if (localSocketAddress != null) {
+                cachedLocalAddress = localSocketAddress.toString();
             }
+            SocketAddress remoteSocketAddress = socket.getRemoteSocketAddress();
+            if (remoteSocketAddress != null) {
+                cachedRemoteAddress = remoteSocketAddress.toString();
+            }
+            cachedCombinedAddress = MllpSocketBuffer.formatAddressString(localSocketAddress, remoteSocketAddress);
 
-            log.debug("Connecting to socket on {}", socketAddress);
-            socket.connect(socketAddress, endpoint.connectTimeout);
+            log.info("checkConnection() - established new connection {}", cachedCombinedAddress);
+            getEndpoint().updateLastConnectionEstablishedTicks();
 
-            log.debug("Creating MllpSocketReader and MllpSocketWriter");
-            mllpSocketReader = new MllpSocketReader(socket, endpoint.receiveTimeout, endpoint.readTimeout, true);
-            if (endpoint.bufferWrites) {
-                mllpSocketWriter = new MllpBufferedSocketWriter(socket, false);
-            } else {
-                mllpSocketWriter = new MllpSocketWriter(socket, false);
+            if (getConfiguration().hasIdleTimeout()) {
+                idleTimeoutExecutor.schedule(this, getConfiguration().getIdleTimeout(), TimeUnit.MILLISECONDS);
             }
+        } else {
+            log.debug("checkConnection() - Connection is still valid - no new connection required");
         }
-        return;
     }
 
-    @ManagedOperation(description = "Check client connection")
-    public boolean managedCheckConnection() {
-        boolean isValid = true;
-        try {
-            checkConnection();
-        } catch (IOException ioEx) {
-            isValid = false;
-            log.debug("JMX check connection: {}", ioEx);
+    /**
+     * Check for idle connection
+     */
+    @Override
+    public synchronized void run() {
+        if (getConfiguration().hasIdleTimeout()) {
+            if (null != socket && !socket.isClosed() && socket.isConnected()) {
+                if (lastProcessCallTicks > 0) {
+                    long idleTime = System.currentTimeMillis() - lastProcessCallTicks;
+                    if (log.isDebugEnabled()) {
+                        log.debug("Checking {} for idle connection: {} - {}", getConnectionAddress(), idleTime, getConfiguration().getIdleTimeout());
+                    }
+                    if (idleTime >= getConfiguration().getIdleTimeout()) {
+                        log.info("MLLP Connection idle time of '{}' milliseconds met or exceeded the idle producer timeout of '{}' milliseconds - resetting conection",
+                            idleTime, getConfiguration().getIdleTimeout());
+                        mllpBuffer.resetSocket(socket);
+                    } else {
+                        long minDelay = 100;
+                        long delay = Long.min(Long.max(minDelay, getConfiguration().getIdleTimeout() - idleTime), getConfiguration().getIdleTimeout());
+                        if (log.isDebugEnabled()) {
+                            log.debug("Scheduling idle producer connection check of {} in {} milliseconds", getConnectionAddress(), delay);
+                        }
+                        idleTimeoutExecutor.schedule(this, delay, TimeUnit.MILLISECONDS);
+                    }
+                } else {
+                    log.debug("Scheduling idle producer connection check in {} milliseconds", getConfiguration().getIdleTimeout());
+                    idleTimeoutExecutor.schedule(this, getConfiguration().getIdleTimeout(), TimeUnit.MILLISECONDS);
+                }
+            }
         }
-        return isValid;
     }
 
+    static class IdleTimeoutThreadFactory implements ThreadFactory {
+        final String endpointKey;
+
+        IdleTimeoutThreadFactory(String endpointKey) {
+            this.endpointKey = endpointKey;
+        }
+
+        public Thread newThread(Runnable r) {
+            Thread timeoutThread = Executors.defaultThreadFactory().newThread(r);
+
+            timeoutThread.setName(String.format("%s[%s]-idle-timeout-thread", MllpTcpClientProducer.class.getSimpleName(), endpointKey));
+
+            return timeoutThread;
+        }
+    }
+
+    @Override
+    public MllpEndpoint getEndpoint() {
+        return (MllpEndpoint) super.getEndpoint();
+    }
+
+    public MllpConfiguration getConfiguration() {
+        return ((MllpEndpoint)this.getEndpoint()).getConfiguration();
+    }
 }
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 d036944..ac2d95d 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
@@ -14,762 +14,195 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.camel.component.mllp;
 
-import java.io.IOException;
-import java.io.InputStream;
 import java.net.BindException;
 import java.net.InetSocketAddress;
 import java.net.ServerSocket;
 import java.net.Socket;
-import java.net.SocketException;
-import java.net.SocketTimeoutException;
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-
-import org.apache.camel.Exchange;
-import org.apache.camel.ExchangePattern;
-import org.apache.camel.Message;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
 import org.apache.camel.Processor;
+import org.apache.camel.api.management.ManagedAttribute;
 import org.apache.camel.api.management.ManagedOperation;
 import org.apache.camel.api.management.ManagedResource;
-import org.apache.camel.component.mllp.impl.Hl7Util;
-import org.apache.camel.component.mllp.impl.MllpBufferedSocketWriter;
-import org.apache.camel.component.mllp.impl.MllpSocketReader;
-import org.apache.camel.component.mllp.impl.MllpSocketUtil;
-import org.apache.camel.component.mllp.impl.MllpSocketWriter;
-import org.apache.camel.converter.IOConverter;
+import org.apache.camel.component.mllp.internal.MllpSocketBuffer;
+import org.apache.camel.component.mllp.internal.TcpServerAcceptRunnable;
+import org.apache.camel.component.mllp.internal.TcpSocketConsumerRunnable;
 import org.apache.camel.impl.DefaultConsumer;
-import org.apache.camel.processor.mllp.Hl7AcknowledgementGenerationException;
 import org.apache.camel.processor.mllp.Hl7AcknowledgementGenerator;
-import org.apache.camel.util.IOHelper;
-import org.slf4j.MDC;
-
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_ACKNOWLEDGEMENT;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_ACKNOWLEDGEMENT_EXCEPTION;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_ACKNOWLEDGEMENT_TYPE;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_AUTO_ACKNOWLEDGE;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_CHARSET;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_CLOSE_CONNECTION_AFTER_SEND;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_CLOSE_CONNECTION_BEFORE_SEND;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_EVENT_TYPE;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_LOCAL_ADDRESS;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_MESSAGE_CONTROL;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_MESSAGE_TYPE;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_PROCESSING_ID;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_RECEIVING_APPLICATION;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_RECEIVING_FACILITY;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_REMOTE_ADDRESS;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_RESET_CONNECTION_AFTER_SEND;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_RESET_CONNECTION_BEFORE_SEND;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_SECURITY;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_SENDING_APPLICATION;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_SENDING_FACILITY;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_TIMESTAMP;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_TRIGGER_EVENT;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_VERSION_ID;
-import static org.apache.camel.component.mllp.MllpEndpoint.SEGMENT_DELIMITER;
 
 /**
  * The MLLP consumer.
  */
-@ManagedResource(description = "MllpTcpServer Consumer")
+@ManagedResource(description = "MLLP Producer")
 public class MllpTcpServerConsumer extends DefaultConsumer {
-    public static final int SOCKET_STARTUP_TEST_WAIT = 100;
-    public static final int SOCKET_STARTUP_TEST_READ_TIMEOUT = 250;
-    ServerSocketThread serverSocketThread;
-
-    List<ClientSocketThread> clientThreads = new LinkedList<>();
-
-    Hl7AcknowledgementGenerator acknowledgementGenerator = new Hl7AcknowledgementGenerator();
+    final ExecutorService acceptExecutor;
+    final ExecutorService consumerExecutor;
+    TcpServerAcceptRunnable acceptRunnable;
+    Map<TcpSocketConsumerRunnable, Long> consumerRunnables = new ConcurrentHashMap<>();
 
-    private final MllpEndpoint endpoint;
 
     public MllpTcpServerConsumer(MllpEndpoint endpoint, Processor processor) {
         super(endpoint, processor);
         log.trace("MllpTcpServerConsumer(endpoint, processor)");
-        this.endpoint = endpoint;
-    }
-
-    @Override
-    protected void doStart() throws Exception {
-        log.debug("doStart() - creating acceptor thread");
-
-        startMllpConsumer();
+        // this.endpoint = endpoint;
+        // this.configuration = endpoint.getConfiguration();
 
-        super.doStart();
+        acceptExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new SynchronousQueue<>());
+        consumerExecutor = new ThreadPoolExecutor(1, getConfiguration().getMaxConcurrentConsumers(), getConfiguration().getAcceptTimeout(), TimeUnit.MILLISECONDS, new SynchronousQueue<>());
     }
 
-    @ManagedOperation(description = "Check server connection")
-    public boolean managedCheckConnection() {
-        boolean isValid = true;
-        try {
-            InetSocketAddress socketAddress;
-            if (null == endpoint.getHostname()) {
-                socketAddress = new InetSocketAddress(endpoint.getPort());
-            } else {
-                socketAddress = new InetSocketAddress(endpoint.getHostname(), endpoint.getPort());
+    @ManagedAttribute(description = "Last activity time")
+    public Map<String, Date> getLastActivityTimes() {
+        Map<String, Date> answer = new HashMap<>();
+
+        for (Map.Entry<TcpSocketConsumerRunnable, Long> entry : consumerRunnables.entrySet()) {
+            TcpSocketConsumerRunnable consumerRunnable = entry.getKey();
+            if (consumerRunnable != null) {
+                answer.put(consumerRunnable.getCombinedAddress(), new Date(entry.getValue()));
             }
-            Socket checkSocket = new Socket();
-            checkSocket.connect(socketAddress, 100);
-            checkSocket.close();
-        } catch (Exception e) {
-            isValid = false;
-            log.debug("JMX check connection: {}", e);
         }
-        return isValid;
+        return answer;
     }
 
-    @ManagedOperation(description = "Starts serverSocket thread and waits for requests")
-    public void startMllpConsumer() throws IOException, InterruptedException {
-        ServerSocket serverSocket = new ServerSocket();
-        if (null != endpoint.receiveBufferSize) {
-            serverSocket.setReceiveBufferSize(endpoint.receiveBufferSize);
-        }
-
-        serverSocket.setReuseAddress(endpoint.reuseAddress);
-
-        // Accept Timeout
-        serverSocket.setSoTimeout(endpoint.acceptTimeout);
+    @ManagedOperation(description = "Close Connections")
+    public void closeConnections() {
 
-        InetSocketAddress socketAddress;
-        if (null == endpoint.getHostname()) {
-            socketAddress = new InetSocketAddress(endpoint.getPort());
-        } else {
-            socketAddress = new InetSocketAddress(endpoint.getHostname(), endpoint.getPort());
+        for (TcpSocketConsumerRunnable consumerRunnable : consumerRunnables.keySet()) {
+            if (consumerRunnable != null) {
+                log.info("Close Connection called via JMX for address {}", consumerRunnable.getCombinedAddress());
+                consumerRunnable.closeSocket();
+            }
         }
-        long startTicks = System.currentTimeMillis();
+    }
 
-        do {
-            try {
-                serverSocket.bind(socketAddress, endpoint.backlog);
-            } catch (BindException bindException) {
-                if (System.currentTimeMillis() > startTicks + endpoint.getBindTimeout()) {
-                    log.error("Failed to bind to address {} within timeout {}", socketAddress, endpoint.getBindTimeout());
-                    throw bindException;
-                } else {
-                    log.warn("Failed to bind to address {} - retrying in {} milliseconds", socketAddress, endpoint.getBindRetryInterval());
-                    Thread.sleep(endpoint.getBindRetryInterval());
-                }
+    @ManagedOperation(description = "Reset Connections")
+    public void resetConnections() {
+
+        for (TcpSocketConsumerRunnable consumerRunnable : consumerRunnables.keySet()) {
+            if (consumerRunnable != null) {
+                log.info("Reset Connection called via JMX for address {}", consumerRunnable.getCombinedAddress());
+                consumerRunnable.resetSocket();
             }
-        } while (!serverSocket.isBound());
+        }
+    }
 
-        serverSocketThread = new ServerSocketThread(serverSocket);
-        serverSocketThread.start();
+    @Override
+    public MllpEndpoint getEndpoint() {
+        return (MllpEndpoint) super.getEndpoint();
     }
 
     @Override
     protected void doStop() throws Exception {
         log.debug("doStop()");
 
-        stopMllpConsumer();
-
-        super.doStop();
-    }
-
-    @ManagedOperation(description = "Stops client threads and serverSocket thread")
-    public void stopMllpConsumer() {
         // Close any client sockets that are currently open
-        for (ClientSocketThread clientSocketThread: clientThreads) {
-            clientSocketThread.interrupt();
+        for (TcpSocketConsumerRunnable consumerClientSocketThread : consumerRunnables.keySet()) {
+            consumerClientSocketThread.stop();
         }
 
+        acceptRunnable.stop();
 
-        switch (serverSocketThread.getState()) {
-        case TERMINATED:
-            // This is what we hope for
-            break;
-        case NEW:
-        case RUNNABLE:
-        case BLOCKED:
-        case WAITING:
-        case TIMED_WAITING:
-        default:
-            serverSocketThread.interrupt();
-            break;
-        }
+        acceptRunnable = null;
 
-        serverSocketThread = null;
+        super.doStop();
     }
 
-    /**
-     * Nested Class to handle the ServerSocket.accept requests
-     */
-    class ServerSocketThread extends Thread {
-        ServerSocket serverSocket;
-
-        ServerSocketThread(ServerSocket serverSocket) {
-            this.setName(createThreadName(serverSocket));
-
-            this.serverSocket = serverSocket;
-        }
-
-        /**
-         * Derive a thread name from the class name, the component URI and the connection information.
-         * <p/>
-         * The String will in the format <class name>[endpoint key] - [local socket address]
-         *
-         * @return String for thread name
-         */
-        String createThreadName(ServerSocket serverSocket) {
-            // Get the classname without the package.  This is a nested class, so we want the parent class name included
-            String fullClassName = this.getClass().getName();
-            String className = fullClassName.substring(fullClassName.lastIndexOf('.') + 1);
-
-            // Get the URI without options
-            String fullEndpointKey = endpoint.getEndpointKey();
-            String endpointKey;
-            if (fullEndpointKey.contains("?")) {
-                endpointKey = fullEndpointKey.substring(0, fullEndpointKey.indexOf('?'));
-            } else {
-                endpointKey = fullEndpointKey;
-            }
-
-            // Now put it all together
-            return String.format("%s[%s] - %s", className, endpointKey, serverSocket.getLocalSocketAddress());
-        }
-
-        /**
-         * The main ServerSocket.accept() loop
-         * <p/>
-         * NOTE:  When a connection is received, the Socket is checked after a brief delay in an attempt to determine
-         * if this is a load-balancer probe.  The test is done before the ClientSocketThread is created to avoid creating
-         * a large number of short lived threads, which is what can occur if the load balancer polling interval is very
-         * short.
-         */
-        public void run() {
-            MDC.put("camel.contextId", endpoint.getCamelContext().getName());
+    @Override
+    protected void doStart() throws Exception {
+        log.debug("doStart() - starting acceptor");
 
-            try {
-                while (!isInterrupted()  &&  null != serverSocket && serverSocket.isBound()  &&  !serverSocket.isClosed()) {
-                    Socket socket = null;
-                    try {
-                        socket = serverSocket.accept();
-                    } catch (SocketTimeoutException timeoutEx) {
-                        // Didn't get a new connection - keep waiting for one
-                        log.debug("Timeout waiting for client connection - keep listening");
-                        continue;
-                    } catch (SocketException socketEx) {
-                        // This should happen if the component is closed while the accept call is blocking
-                        if (serverSocket.isBound()) {
-                            try {
-                                serverSocket.close();
-                            } catch (Exception ex) {
-                                log.debug("Exception encountered closing ServerSocket after SocketException on accept() - ignoring", ex);
-                            }
-                        }
-                        continue;
-                    } catch (IOException ioEx) {
-                        log.error("Exception encountered accepting connection - closing ServerSocket", ioEx);
-                        if (serverSocket.isBound()) {
-                            try {
-                                serverSocket.close();
-                            } catch (Exception ex) {
-                                log.debug("Exception encountered closing ServerSocket after exception on accept() - ignoring", ex);
-                            }
-                        }
-                        continue;
-                    }
-
-                    try {
-                    /* Wait a bit and then check and see if the socket is really there - it could be a load balancer
-                     pinging the port
-                      */
-                        if (socket.isConnected() && !socket.isClosed()) {
-                            log.debug("Socket appears to be there - checking for available data in {} milliseconds", SOCKET_STARTUP_TEST_WAIT);
-                            Thread.sleep(SOCKET_STARTUP_TEST_WAIT);
-
-                            InputStream inputStream;
-                            try {
-                                inputStream = socket.getInputStream();
-                            } catch (IOException ioEx) {
-                                MllpSocketUtil.reset(socket, log, "Failed to retrieve the InputStream for socket after the initial connection was accepted");
-                                continue;
-                            }
-
-                            if (0 < inputStream.available()) {
-                                // Something is there - start the client thread
-                                ClientSocketThread clientThread = new ClientSocketThread(socket, null);
-                                clientThreads.add(clientThread);
-                                clientThread.start();
-                                continue;
-                            }
-
-                            // The easy check failed - so trigger a blocking read
-                            MllpSocketUtil.setSoTimeout(socket, SOCKET_STARTUP_TEST_READ_TIMEOUT, log, "Preparing to check for available data on component startup");
-                            try {
-                                int tmpByte = inputStream.read();
-                                if (-1 == tmpByte) {
-                                    log.debug("Check for available data failed - Socket.read() returned END_OF_STREAM");
-                                    MllpSocketUtil.close(socket, null, null);
-                                } else {
-                                    ClientSocketThread clientThread = new ClientSocketThread(socket, tmpByte);
-                                    clientThreads.add(clientThread);
-                                    clientThread.start();
-                                }
-                            } catch (SocketTimeoutException timeoutEx) {
-                                // No data, but the socket is there
-                                String logMessageFormat =
-                                    "Check for available data failed - Socket.read() timed-out after {} milliseconds."
-                                        + "  No Data - but the socket is there.  Starting ClientSocketThread";
-                                log.debug(logMessageFormat, SOCKET_STARTUP_TEST_READ_TIMEOUT);
-                                ClientSocketThread clientThread = new ClientSocketThread(socket, null);
-                                clientThreads.add(clientThread);
-                                clientThread.start();
-                            } catch (IOException ioEx) {
-                                log.debug("Ignoring IOException encountered when attempting to read a byte - connection was reset");
-                                try {
-                                    socket.close();
-                                } catch (IOException closeEx) {
-                                    log.debug("Ignoring IOException encountered when attempting to close the connection after the connection reset was detected", closeEx);
-                                }
-                            }
-                        }
-                    } catch (SocketTimeoutException timeoutEx) {
-                        // No new clients
-                        log.trace("SocketTimeoutException waiting for new connections - no new connections");
-
-                        for (int i = clientThreads.size() - 1; i >= 0; --i) {
-                            ClientSocketThread thread = clientThreads.get(i);
-                            if (!thread.isAlive()) {
-                                clientThreads.remove(i);
-                            }
-                        }
-                    } catch (InterruptedException interruptEx) {
-                        log.debug("accept loop interrupted - closing ServerSocket");
-                        try {
-                            serverSocket.close();
-                        } catch (Exception ex) {
-                            log.debug("Exception encountered closing ServerSocket after InterruptedException - ignoring", ex);
-                        }
-                    } catch (Exception ex) {
-                        log.error("Exception accepting new connection - retrying", ex);
-                    }
-                }
-            } finally {
-                log.debug("ServerSocket.accept loop finished - closing listener");
-                if (null != serverSocket && serverSocket.isBound() && !serverSocket.isClosed()) {
-                    try {
-                        serverSocket.close();
-                    } catch (Exception ex) {
-                        log.debug("Exception encountered closing ServerSocket after accept loop had exited - ignoring", ex);
-                    }
-                }
-            }
+        ServerSocket serverSocket = new ServerSocket();
+        if (getConfiguration().hasReceiveBufferSize()) {
+            serverSocket.setReceiveBufferSize(getConfiguration().getReceiveBufferSize());
         }
 
-        @Override
-        public void interrupt() {
-            super.interrupt();
-            if (null != serverSocket) {
-                if (serverSocket.isBound()) {
-                    try {
-                        serverSocket.close();
-                    } catch (IOException ioEx) {
-                        log.warn("Exception encountered closing ServerSocket in interrupt() method - ignoring", ioEx);
-                    }
-                }
-            }
+        if (getConfiguration().hasReuseAddress()) {
+            serverSocket.setReuseAddress(getConfiguration().getReuseAddress());
         }
-    }
 
-    /**
-     * Nested Class reads the Socket
-     */
-    class ClientSocketThread extends Thread {
-        final Socket clientSocket;
-        final MllpSocketReader mllpSocketReader;
-        final MllpSocketWriter mllpSocketWriter;
-
-        Integer initialByte;
-
-        ClientSocketThread(Socket clientSocket, Integer initialByte) throws IOException {
-            this.initialByte = initialByte;
-            this.setName(createThreadName(clientSocket));
-            this.clientSocket = clientSocket;
-            this.clientSocket.setKeepAlive(endpoint.keepAlive);
-            this.clientSocket.setTcpNoDelay(endpoint.tcpNoDelay);
-            if (null != endpoint.receiveBufferSize) {
-                this.clientSocket.setReceiveBufferSize(endpoint.receiveBufferSize);
-            }
-            if (null != endpoint.sendBufferSize) {
-                this.clientSocket.setSendBufferSize(endpoint.sendBufferSize);
-            }
-            this.clientSocket.setReuseAddress(endpoint.reuseAddress);
-            this.clientSocket.setSoLinger(false, -1);
-
-            // Initial Read Timeout
-            MllpSocketUtil.setSoTimeout(clientSocket, endpoint.receiveTimeout, log, "Constructing ClientSocketThread");
+        // Accept Timeout
+        serverSocket.setSoTimeout(getConfiguration().getAcceptTimeout());
 
-            mllpSocketReader = new MllpSocketReader(this.clientSocket, endpoint.receiveTimeout, endpoint.readTimeout, false);
-            if (endpoint.bufferWrites) {
-                mllpSocketWriter = new MllpBufferedSocketWriter(this.clientSocket, true);
-            } else {
-                mllpSocketWriter = new MllpSocketWriter(this.clientSocket, true);
-            }
+        InetSocketAddress socketAddress;
+        if (null == getEndpoint().getHostname()) {
+            socketAddress = new InetSocketAddress(getEndpoint().getPort());
+        } else {
+            socketAddress = new InetSocketAddress(getEndpoint().getHostname(), getEndpoint().getPort());
         }
+        long startTicks = System.currentTimeMillis();
 
-        /**
-         * derive a thread name from the class name, the component URI and the connection information
-         * <p/>
-         * The String will in the format <class name>[endpoint key] - [local socket address] -> [remote socket address]
-         *
-         * @return the thread name
-         */
-        String createThreadName(Socket socket) {
-            // Get the classname without the package.  This is a nested class, so we want the parent class name included
-            String fullClassName = this.getClass().getName();
-            String className = fullClassName.substring(fullClassName.lastIndexOf('.') + 1);
-
-            // Get the URI without options
-            String fullEndpointKey = endpoint.getEndpointKey();
-            String endpointKey;
-            if (fullEndpointKey.contains("?")) {
-                endpointKey = fullEndpointKey.substring(0, fullEndpointKey.indexOf('?'));
+        // Log usage of deprecated URI options
+        if (getConfiguration().hasMaxReceiveTimeouts()) {
+            if (getConfiguration().hasIdleTimeout()) {
+                log.info("Both maxReceivedTimeouts {} and idleTimeout {} URI options are specified - idleTimeout will be used", getConfiguration().getMaxReceiveTimeouts(), getConfiguration().getIdleTimeout());
             } else {
-                endpointKey = fullEndpointKey;
-            }
-
-            // Now put it all together
-            return String.format("%s[%s] - %s -> %s", className, endpointKey, socket.getLocalSocketAddress(), socket.getRemoteSocketAddress());
-        }
-
-        @Override
-        public void run() {
-            int receiveTimeoutCounter = 0;
-            MDC.put("camel.contextId", endpoint.getCamelContext().getName());
-
-            while (!isInterrupted()  &&  null != clientSocket  &&  clientSocket.isConnected()  &&  !clientSocket.isClosed()) {
-                byte[] hl7MessageBytes = null;
-
-                log.debug("Checking for data ....");
-                try {
-                    hl7MessageBytes = mllpSocketReader.readEnvelopedPayload(initialByte);
-                    if (hl7MessageBytes == null) {
-                        // No data received - check for max timeouts
-                        if (endpoint.maxReceiveTimeouts > 0 && ++receiveTimeoutCounter >= endpoint.maxReceiveTimeouts) {
-                            String reasonMessage = String.format("Idle Client after %d receive timeouts [%d-milliseconds] - resetting connection", receiveTimeoutCounter, endpoint.receiveTimeout);
-                            MllpSocketUtil.reset(clientSocket, log, reasonMessage);
-                        }
-                        continue;
-                    }
-                } catch (MllpException mllpEx) {
-                    Exchange exchange = endpoint.createExchange(ExchangePattern.InOut);
-                    exchange.setException(mllpEx);
-                    log.warn("Exception encountered reading payload - sending exception to route", mllpEx);
-                    try {
-                        getProcessor().process(exchange);
-                    } catch (Exception e) {
-                        log.error("Exception encountered processing exchange with exception encounter reading payload", e);
-                    }
-                    continue;
-                } finally {
-                    initialByte = null;
-                }
-
-                // Send the message on for processing and wait for the response
-                log.debug("Populating the exchange with received message");
-                Exchange exchange = endpoint.createExchange(ExchangePattern.InOut);
-                try {
-                    createUoW(exchange);
-                    Message message = exchange.getIn();
-                    message.setBody(hl7MessageBytes, byte[].class);
-
-                    message.setHeader(MLLP_LOCAL_ADDRESS, clientSocket.getLocalAddress().toString());
-                    message.setHeader(MLLP_REMOTE_ADDRESS, clientSocket.getRemoteSocketAddress());
-                    message.setHeader(MLLP_AUTO_ACKNOWLEDGE, endpoint.autoAck);
-
-                    if (endpoint.validatePayload) {
-                        String exceptionMessage = Hl7Util.generateInvalidPayloadExceptionMessage(hl7MessageBytes);
-                        if (exceptionMessage != null) {
-                            exchange.setException(new MllpInvalidMessageException(exceptionMessage, hl7MessageBytes));
-                        }
-                    }
-                    populateHl7DataHeaders(exchange, message, hl7MessageBytes);
-
-                    log.debug("Calling processor");
-                    try {
-                        getProcessor().process(exchange);
-                        sendAcknowledgement(hl7MessageBytes, exchange);
-                    } catch (RuntimeException runtimeEx) {
-                        throw runtimeEx;
-                    } catch (Exception ex) {
-                        log.error("Unexpected exception processing exchange", ex);
-                    }
-                } catch (Exception uowEx) {
-                    // TODO:  Handle this correctly
-                    exchange.setException(uowEx);
-                    log.warn("Exception encountered creating Unit of Work - sending exception to route", uowEx);
-                    try {
-                        getProcessor().process(exchange);
-                    } catch (Exception e) {
-                        log.error("Exception encountered processing exchange with exception encountered createing Unit of Work", e);
-                    }
-                    continue;
-                } finally {
-                    if (exchange != null) {
-                        doneUoW(exchange);
-                    }
-                }
-
-
+                getConfiguration().setIdleTimeout(getConfiguration().getMaxReceiveTimeouts() * getConfiguration().getReceiveTimeout());
+                log.info("Deprecated URI option maxReceivedTimeouts {} specified - idleTimeout {} will be used", getConfiguration().getMaxReceiveTimeouts(), getConfiguration().getIdleTimeout());
             }
-
-            log.debug("ClientSocketThread exiting");
         }
 
-        private void sendAcknowledgement(byte[] originalHl7MessageBytes, Exchange exchange) {
-            log.info("sendAcknowledgement");
-
-            // Check BEFORE_SEND Properties
-            if (exchange.getProperty(MLLP_RESET_CONNECTION_BEFORE_SEND, boolean.class)) {
-                String reasonMessage = String.format("Exchange property %s is %b", MLLP_RESET_CONNECTION_BEFORE_SEND,  exchange.getProperty(MLLP_RESET_CONNECTION_BEFORE_SEND, boolean.class));
-                MllpSocketUtil.reset(clientSocket, log, reasonMessage);
-                return;
-            } else if (exchange.getProperty(MLLP_CLOSE_CONNECTION_BEFORE_SEND, boolean.class)) {
-                String reasonMessage = String.format("Exchange property %s is %b", MLLP_CLOSE_CONNECTION_BEFORE_SEND,  exchange.getProperty(MLLP_CLOSE_CONNECTION_BEFORE_SEND, boolean.class));
-                MllpSocketUtil.close(clientSocket, log, reasonMessage);
-                return;
-            }
-
-            // Find the acknowledgement body
-            // TODO:  Enhance this to say whether or not the acknowledgment is missing or just of an un-convertible type
-            byte[] acknowledgementMessageBytes = exchange.getProperty(MLLP_ACKNOWLEDGEMENT, byte[].class);
-            String acknowledgementMessageType = null;
-            if (null == acknowledgementMessageBytes) {
-                boolean autoAck = exchange.getProperty(MLLP_AUTO_ACKNOWLEDGE, true, boolean.class);
-                if (!autoAck) {
-                    exchange.setException(new MllpInvalidAcknowledgementException("Automatic Acknowledgement is disabled and the "
-                            + MLLP_ACKNOWLEDGEMENT + " exchange property is null or cannot be converted to byte[]", originalHl7MessageBytes, acknowledgementMessageBytes));
-                    return;
-                }
-
-                String acknowledgmentTypeProperty = exchange.getProperty(MLLP_ACKNOWLEDGEMENT_TYPE, String.class);
-                try {
-                    if (null == acknowledgmentTypeProperty) {
-                        if (null == exchange.getException()) {
-                            acknowledgementMessageType = "AA";
-                            acknowledgementMessageBytes = acknowledgementGenerator.generateApplicationAcceptAcknowledgementMessage(originalHl7MessageBytes);
-                        } else {
-                            acknowledgementMessageType = "AE";
-                            acknowledgementMessageBytes = acknowledgementGenerator.generateApplicationErrorAcknowledgementMessage(originalHl7MessageBytes);
-                        }
-                    } else {
-                        switch (acknowledgmentTypeProperty) {
-                        case "AA":
-                            acknowledgementMessageType = "AA";
-                            acknowledgementMessageBytes = acknowledgementGenerator.generateApplicationAcceptAcknowledgementMessage(originalHl7MessageBytes);
-                            break;
-                        case "AE":
-                            acknowledgementMessageType = "AE";
-                            acknowledgementMessageBytes = acknowledgementGenerator.generateApplicationErrorAcknowledgementMessage(originalHl7MessageBytes);
-                            break;
-                        case "AR":
-                            acknowledgementMessageType = "AR";
-                            acknowledgementMessageBytes = acknowledgementGenerator.generateApplicationRejectAcknowledgementMessage(originalHl7MessageBytes);
-                            break;
-                        default:
-                            exchange.setException(new Hl7AcknowledgementGenerationException("Unsupported acknowledgment type: " + acknowledgmentTypeProperty));
-                            return;
-                        }
-                    }
-                } catch (Hl7AcknowledgementGenerationException ackGenerationException) {
-                    exchange.setProperty(MLLP_ACKNOWLEDGEMENT_EXCEPTION, ackGenerationException);
-                    exchange.setException(ackGenerationException);
+        do {
+            try {
+                if (getConfiguration().hasBacklog()) {
+                    serverSocket.bind(socketAddress, getConfiguration().getBacklog());
+                } else {
+                    serverSocket.bind(socketAddress);
                 }
-            } else {
-                final byte bM = 77;
-                final byte bS = 83;
-                final byte bA = 65;
-                final byte bE = 69;
-                final byte bR = 82;
-
-                final byte fieldSeparator = originalHl7MessageBytes[3];
-                // Acknowledgment is specified in exchange property - determine the acknowledgement type
-                for (int i = 0; i < originalHl7MessageBytes.length; ++i) {
-                    if (SEGMENT_DELIMITER == i) {
-                        if (i + 7 < originalHl7MessageBytes.length // Make sure we don't run off the end of the message
-                                && bM == originalHl7MessageBytes[i + 1] && bS == originalHl7MessageBytes[i + 2]
-                                && bA == originalHl7MessageBytes[i + 3] && fieldSeparator == originalHl7MessageBytes[i + 4]) {
-                            if (fieldSeparator != originalHl7MessageBytes[i + 7]) {
-                                log.warn("MSA-1 is longer than 2-bytes - ignoring trailing bytes");
-                            }
-                            // Found MSA - pull acknowledgement bytes
-                            byte[] acknowledgmentTypeBytes = new byte[2];
-                            acknowledgmentTypeBytes[0] = originalHl7MessageBytes[i + 5];
-                            acknowledgmentTypeBytes[1] = originalHl7MessageBytes[i + 6];
-                            try {
-                                acknowledgementMessageType = IOConverter.toString(acknowledgmentTypeBytes, exchange);
-                            } catch (IOException ioEx) {
-                                throw new RuntimeException("Failed to convert acknowledgement message to string", ioEx);
-                            }
-
-                            // Verify it's a valid acknowledgement code
-                            if (bA != acknowledgmentTypeBytes[0]) {
-                                switch (acknowledgementMessageBytes[1]) {
-                                case bA:
-                                case bR:
-                                case bE:
-                                    break;
-                                default:
-                                    log.warn("Invalid acknowledgement type [" + acknowledgementMessageType + "] found in message - should be AA, AE or AR");
-                                }
-                            }
-
-                            // if the MLLP_ACKNOWLEDGEMENT_TYPE property is set on the exchange, make sure it matches
-                            String acknowledgementTypeProperty = exchange.getProperty(MLLP_ACKNOWLEDGEMENT_TYPE, String.class);
-                            if (null != acknowledgementTypeProperty && !acknowledgementTypeProperty.equals(acknowledgementMessageType)) {
-                                log.warn("Acknowledgement type found in message [" + acknowledgementMessageType + "] does not match "
-                                        + MLLP_ACKNOWLEDGEMENT_TYPE + " exchange property value [" + acknowledgementTypeProperty + "] - using value found in message");
-                            }
-                        }
-                    }
+            } catch (BindException bindException) {
+                if (System.currentTimeMillis() > startTicks + getConfiguration().getBindTimeout()) {
+                    log.error("Failed to bind to address {} within timeout {}", socketAddress, getConfiguration().getBindTimeout());
+                    throw bindException;
+                } else {
+                    log.warn("Failed to bind to address {} - retrying in {} milliseconds", socketAddress, getConfiguration().getBindRetryInterval());
+                    Thread.sleep(getConfiguration().getBindRetryInterval());
                 }
             }
+        } while (!serverSocket.isBound());
 
-            Message message;
-            if (exchange.hasOut()) {
-                message = exchange.getOut();
-            } else {
-                message = exchange.getIn();
-            }
-            message.setHeader(MLLP_ACKNOWLEDGEMENT, acknowledgementMessageBytes);
-            // TODO:  Use the charset of the exchange
-            message.setHeader(MLLP_ACKNOWLEDGEMENT_STRING, new String(acknowledgementMessageBytes));
-            message.setHeader(MLLP_ACKNOWLEDGEMENT_TYPE, acknowledgementMessageType);
+        acceptRunnable = new TcpServerAcceptRunnable(this, serverSocket);
+        acceptExecutor.submit(acceptRunnable);
 
-            // Send the acknowledgement
-            log.debug("Sending Acknowledgement: {}", MllpComponent.covertBytesToPrintFriendlyString(acknowledgementMessageBytes));
-            try {
-                mllpSocketWriter.writeEnvelopedPayload(originalHl7MessageBytes, acknowledgementMessageBytes);
-            } catch (MllpException mllpEx) {
-                log.error("MLLP Acknowledgement failure: {}", mllpEx);
-                MllpAcknowledgementDeliveryException deliveryException = new MllpAcknowledgementDeliveryException(originalHl7MessageBytes, acknowledgementMessageBytes, mllpEx);
-                exchange.setProperty(MLLP_ACKNOWLEDGEMENT_EXCEPTION, deliveryException);
-                exchange.setException(deliveryException);
-            }
+        super.doStart();
+    }
 
-            // Check AFTER_SEND Properties
-            if (exchange.getProperty(MLLP_RESET_CONNECTION_AFTER_SEND, boolean.class)) {
-                String reasonMessage = String.format("Exchange property %s is %b", MLLP_RESET_CONNECTION_AFTER_SEND,  exchange.getProperty(MLLP_RESET_CONNECTION_AFTER_SEND, boolean.class));
-                MllpSocketUtil.reset(clientSocket, log, reasonMessage);
-                return;
-            } else if (exchange.getProperty(MLLP_CLOSE_CONNECTION_AFTER_SEND, boolean.class)) {
-                String reasonMessage = String.format("Exchange property %s is %b", MLLP_CLOSE_CONNECTION_AFTER_SEND,  exchange.getProperty(MLLP_CLOSE_CONNECTION_AFTER_SEND, boolean.class));
-                MllpSocketUtil.reset(clientSocket, log, reasonMessage);
-            }
-        }
+    @Override
+    protected void doShutdown() throws Exception {
+        super.doShutdown();
+        consumerExecutor.shutdownNow();
+        acceptExecutor.shutdownNow();
+    }
 
-        private void populateHl7DataHeaders(Exchange exchange, Message message, byte[] hl7MessageBytes) {
-            if (hl7MessageBytes == null ||  hl7MessageBytes.length < 8) {
-                // Not enough data to populate anything - just return
-                return;
-            }
-            // Find the end of the MSH and indexes of the fields in the MSH to populate message headers
-            final byte fieldSeparator = hl7MessageBytes[3];
-            int endOfMSH = -1;
-            List<Integer> fieldSeparatorIndexes = new ArrayList<>(10);  // We should have at least 10 fields
-
-            for (int i = 0; i < hl7MessageBytes.length; ++i) {
-                if (fieldSeparator == hl7MessageBytes[i]) {
-                    fieldSeparatorIndexes.add(i);
-                } else if (SEGMENT_DELIMITER == hl7MessageBytes[i]) {
-                    // If the MSH Segment doesn't have a trailing field separator, add one so the field can be extracted into a header
-                    if (fieldSeparator != hl7MessageBytes[i - 1]) {
-                        fieldSeparatorIndexes.add(i);
-                    }
-                    endOfMSH = i;
-                    break;
-                }
-            }
+    public MllpConfiguration getConfiguration() {
+        return getEndpoint().getConfiguration();
+    }
 
-            String messageBodyForDebugging = new String(hl7MessageBytes);
-            if (-1 == endOfMSH) {
-                // TODO:  May want to throw some sort of an Exception here
-                log.error("Population of message headers failed - unable to find the end of the MSH segment");
-            } else if (endpoint.hl7Headers) {
-                log.debug("Populating the HL7 message headers");
-                Charset charset = Charset.forName(IOHelper.getCharsetName(exchange));
-
-                for (int i = 2; i < fieldSeparatorIndexes.size(); ++i) {
-                    int startingFieldSeparatorIndex = fieldSeparatorIndexes.get(i - 1);
-                    int endingFieldSeparatorIndex = fieldSeparatorIndexes.get(i);
-
-                    // Only populate the header if there's data in the HL7 field
-                    if (endingFieldSeparatorIndex - startingFieldSeparatorIndex > 1) {
-                        String headerName = null;
-                        switch (i) {
-                        case 2: // MSH-3
-                            headerName = MLLP_SENDING_APPLICATION;
-                            break;
-                        case 3: // MSH-4
-                            headerName = MLLP_SENDING_FACILITY;
-                            break;
-                        case 4: // MSH-5
-                            headerName = MLLP_RECEIVING_APPLICATION;
-                            break;
-                        case 5: // MSH-6
-                            headerName = MLLP_RECEIVING_FACILITY;
-                            break;
-                        case 6: // MSH-7
-                            headerName = MLLP_TIMESTAMP;
-                            break;
-                        case 7: // MSH-8
-                            headerName = MLLP_SECURITY;
-                            break;
-                        case 8: // MSH-9
-                            headerName = MLLP_MESSAGE_TYPE;
-                            break;
-                        case 9: // MSH-10
-                            headerName = MLLP_MESSAGE_CONTROL;
-                            break;
-                        case 10: // MSH-11
-                            headerName = MLLP_PROCESSING_ID;
-                            break;
-                        case 11: // MSH-12
-                            headerName = MLLP_VERSION_ID;
-                            break;
-                        case 17: // MSH-18
-                            headerName = MLLP_CHARSET;
-                            break;
-                        default:
-                            // Not processing this field
-                            continue;
-                        }
-
-                        String headerValue = new String(hl7MessageBytes, startingFieldSeparatorIndex + 1,
-                                endingFieldSeparatorIndex - startingFieldSeparatorIndex - 1,
-                                charset);
-                        message.setHeader(headerName, headerValue);
-
-                        // For MSH-9, set a couple more headers
-                        if (i == 8) {
-                            // final byte componentSeparator = hl7MessageBytes[4];
-                            String componentSeparator = new String(hl7MessageBytes, 4, 1, charset);
-                            String[] components = headerValue.split(String.format("\\Q%s\\E", componentSeparator), 3);
-                            message.setHeader(MLLP_EVENT_TYPE, components[0]);
-                            if (2 <= components.length) {
-                                message.setHeader(MLLP_TRIGGER_EVENT, components[1]);
-                            }
-                        }
-                    }
-                }
-            } else {
-                log.trace("HL7 Message headers disabled");
-            }
+    public Map<TcpSocketConsumerRunnable, Long> getConsumerRunnables() {
+        return consumerRunnables;
+    }
 
-        }
+    public void startConsumer(Socket clientSocket) {
+        TcpSocketConsumerRunnable client = new TcpSocketConsumerRunnable(this, clientSocket);
 
-        @Override
-        public void interrupt() {
-            if (null != clientSocket  &&  clientSocket.isConnected()  && !clientSocket.isClosed()) {
-                MllpSocketUtil.close(clientSocket, log, this.getClass().getSimpleName() + " interrupted");
-            }
-            super.interrupt();
+        consumerRunnables.put(client, System.currentTimeMillis());
+        try {
+            log.info("Starting consumer for Socket {}", clientSocket);
+            consumerExecutor.submit(client);
+        } catch (RejectedExecutionException rejectedExecutionEx) {
+            log.warn("Cannot start consumer - max consumers already active");
+            getEndpoint().doConnectionClose(clientSocket, true, null);
         }
     }
+
 }
 
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpTimeoutException.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpTimeoutException.java
deleted file mode 100644
index b1e4906..0000000
--- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpTimeoutException.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * 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;
-
-/**
- * Raised when a MLLP Producer or Consumer encounter a timeout reading a message or an acknowledgment
- */
-public class MllpTimeoutException extends MllpException {
-    static final String EXCEPTION_MESSAGE = "Timeout receiving HL7 Message";
-
-    public MllpTimeoutException(byte[] partialHl7Message) {
-        super(EXCEPTION_MESSAGE, partialHl7Message);
-    }
-
-    public MllpTimeoutException(String message, byte[] partialHl7Message) {
-        super(message, partialHl7Message);
-    }
-
-    public MllpTimeoutException(byte[] partialHl7Message, Throwable cause) {
-        super(EXCEPTION_MESSAGE, partialHl7Message, cause);
-    }
-
-    public MllpTimeoutException(String message, byte[] partialHl7Message, Throwable cause) {
-        super(message, partialHl7Message, cause);
-    }
-
-    protected MllpTimeoutException(String message, byte[] hl7Message, byte[] partialHl7Acknowledgement) {
-        super(message, hl7Message, partialHl7Acknowledgement);
-    }
-
-    protected MllpTimeoutException(String message, byte[] hl7Message, byte[] partialHl7Acknowledgement, Throwable cause) {
-        super(message, hl7Message, partialHl7Acknowledgement, cause);
-    }
-
-    /**
-     * Get the HL7 message payload associated with this exception, if any.
-     *
-     * @return If the timeout occurred while attempting to receive an HL7 Message, this will be null.  If the timeout
-     * occurred while attempting to receive an HL7 Acknowledgement, this will be the HL7 Message.  If the timeout occurred
-     * while attempting to complete the read of an HL7 message (i.e. part of the message has already been read), this
-     * will be the partial acknowledgement payload that was read before the timeout.
-     */
-    public byte[] getHl7Message() {
-        return super.getHl7Message();
-    }
-
-}
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpWriteException.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpWriteException.java
index 2661d7c..9029bdd 100644
--- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpWriteException.java
+++ b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/MllpWriteException.java
@@ -14,6 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.camel.component.mllp;
 
 /**
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/impl/Hl7Util.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/impl/Hl7Util.java
deleted file mode 100644
index f4a8f9b..0000000
--- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/impl/Hl7Util.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/**
- * 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.impl;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import static org.apache.camel.component.mllp.MllpEndpoint.END_OF_BLOCK;
-import static org.apache.camel.component.mllp.MllpEndpoint.MESSAGE_TERMINATOR;
-import static org.apache.camel.component.mllp.MllpEndpoint.SEGMENT_DELIMITER;
-import static org.apache.camel.component.mllp.MllpEndpoint.START_OF_BLOCK;
-
-public final class Hl7Util {
-
-    static final Logger LOG = LoggerFactory.getLogger(Hl7Util.class);
-
-    private Hl7Util() {
-    }
-
-    public static String generateInvalidPayloadExceptionMessage(final byte[] hl7Bytes) {
-        if (hl7Bytes == null) {
-            return "HL7 payload is null";
-        }
-
-        return generateInvalidPayloadExceptionMessage(hl7Bytes, hl7Bytes.length);
-    }
-
-    /**
-     * Verifies that the HL7 payload array
-     * <p>
-     * The MLLP protocol does not allow embedded START_OF_BLOCK or END_OF_BLOCK characters.  The END_OF_DATA character
-     * is allowed (and expected) because it is also the segment delimiter for an HL7 message
-     *
-     * @param hl7Bytes the HL7 payload to validate
-     * @return If the payload is invalid, an error message suitable for inclusion in an exception is returned.  If
-     * the payload is valid, null is returned;
-     */
-    public static String generateInvalidPayloadExceptionMessage(final byte[] hl7Bytes, final int length) {
-        if (hl7Bytes == null) {
-            return "HL7 payload is null";
-        }
-
-        if (hl7Bytes.length <= 0) {
-            return "HL7 payload is empty";
-        }
-
-        if (length > hl7Bytes.length) {
-            LOG.warn("The length specified for the HL7 payload array <{}> is greater than the actual length of the array <{}> - only validating {} bytes", length, hl7Bytes.length, length);
-        }
-
-        if (hl7Bytes.length < 3 || hl7Bytes[0] != 'M' || hl7Bytes[1] != 'S' || hl7Bytes[2] != 'H') {
-            return String.format("The first segment of the HL7 payload {%s} is not an MSH segment", new String(hl7Bytes, 0, Math.min(3, hl7Bytes.length)));
-        }
-
-        int validationLength = Math.min(length, hl7Bytes.length);
-
-        if (hl7Bytes[validationLength - 2] != SEGMENT_DELIMITER || hl7Bytes[validationLength - 1] != 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) SEGMENT_DELIMITER, (byte) MESSAGE_TERMINATOR);
-        }
-
-        for (int i = 0; i < validationLength; ++i) {
-            switch (hl7Bytes[i]) {
-            case START_OF_BLOCK:
-                return String.format("HL7 payload contains an embedded START_OF_BLOCK {%#x, ASCII <VT>} at index %d", hl7Bytes[i], i);
-            case END_OF_BLOCK:
-                return String.format("HL7 payload contains an embedded END_OF_BLOCK {%#x, ASCII <FS>} at index %d", hl7Bytes[i], i);
-            default:
-                // continue on
-            }
-        }
-
-        return null;
-    }
-
-
-}
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/impl/MllpBufferedSocketWriter.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/impl/MllpBufferedSocketWriter.java
deleted file mode 100644
index 6f52543..0000000
--- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/impl/MllpBufferedSocketWriter.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/**
- * 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.impl;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.net.Socket;
-import java.net.SocketException;
-
-import org.apache.camel.component.mllp.MllpAcknowledgementDeliveryException;
-import org.apache.camel.component.mllp.MllpException;
-import org.apache.camel.component.mllp.MllpWriteException;
-
-import static org.apache.camel.component.mllp.MllpEndpoint.END_OF_BLOCK;
-import static org.apache.camel.component.mllp.MllpEndpoint.END_OF_DATA;
-import static org.apache.camel.component.mllp.MllpEndpoint.START_OF_BLOCK;
-
-public class MllpBufferedSocketWriter extends MllpSocketWriter {
-
-    static final int DEFAULT_SO_SNDBUF = 65535;
-
-    ByteArrayOutputStream outputBuffer;
-
-    public MllpBufferedSocketWriter(Socket socket, boolean acknowledgementWriter) {
-        super(socket, acknowledgementWriter);
-        try {
-            outputBuffer = new ByteArrayOutputStream(socket.getSendBufferSize());
-        } catch (SocketException socketEx) {
-            log.warn(String.format("Ignoring exception encountered retrieving SO_SNDBUF from the socket - using default size of %d bytes", DEFAULT_SO_SNDBUF), socketEx);
-            outputBuffer = new ByteArrayOutputStream(DEFAULT_SO_SNDBUF);
-        }
-    }
-
-    @Override
-    public void writeEnvelopedPayload(byte[] hl7MessageBytes, byte[] hl7AcknowledgementBytes) throws MllpException {
-        if (socket == null) {
-            final String errorMessage = "Socket is null";
-            if (isAcknowledgementWriter()) {
-                throw new MllpAcknowledgementDeliveryException(errorMessage, hl7MessageBytes, hl7AcknowledgementBytes);
-            } else {
-                throw new MllpWriteException(errorMessage, hl7MessageBytes);
-            }
-        } else if (!socket.isConnected()) {
-            final String errorMessage = "Socket is not connected";
-            if (isAcknowledgementWriter()) {
-                throw new MllpAcknowledgementDeliveryException(errorMessage, hl7MessageBytes, hl7AcknowledgementBytes);
-            } else {
-                throw new MllpWriteException(errorMessage, hl7MessageBytes);
-            }
-        } else if (socket.isClosed()) {
-            final String errorMessage = "Socket is closed";
-            if (isAcknowledgementWriter()) {
-                throw new MllpAcknowledgementDeliveryException(errorMessage, hl7MessageBytes, hl7AcknowledgementBytes);
-            } else {
-                throw new MllpWriteException(errorMessage, hl7MessageBytes);
-            }
-        }
-
-        OutputStream socketOutputStream = null;
-        try {
-            socketOutputStream = socket.getOutputStream();
-        } catch (IOException e) {
-            final String errorMessage = "Failed to retrieve the OutputStream from the Socket";
-            if (isAcknowledgementWriter()) {
-                throw new MllpAcknowledgementDeliveryException(errorMessage, hl7MessageBytes, hl7AcknowledgementBytes);
-            } else {
-                throw new MllpWriteException(errorMessage, hl7MessageBytes, hl7AcknowledgementBytes);
-            }
-        }
-
-        outputBuffer.write(START_OF_BLOCK);
-
-        if (isAcknowledgementWriter()) {
-            if (hl7AcknowledgementBytes == null) {
-                log.warn("HL7 Acknowledgement payload is null - sending empty MLLP payload");
-            } else if (hl7AcknowledgementBytes.length <= 0) {
-                log.warn("HL7 Acknowledgement payload is empty - sending empty MLLP payload");
-            } else {
-                outputBuffer.write(hl7AcknowledgementBytes, 0, hl7AcknowledgementBytes.length);
-            }
-        } else {
-            if (hl7MessageBytes == null) {
-                log.warn("HL7 Message payload is null - sending empty MLLP payload");
-            } else if (hl7MessageBytes.length <= 0) {
-                log.warn("HL7 Message payload is empty - sending empty MLLP payload");
-            } else {
-                outputBuffer.write(hl7MessageBytes, 0, hl7MessageBytes.length);
-            }
-        }
-
-        outputBuffer.write(END_OF_BLOCK);
-        outputBuffer.write(END_OF_DATA);
-
-        try {
-            outputBuffer.writeTo(socketOutputStream);
-            socketOutputStream.flush();
-        } catch (IOException e) {
-            final String errorMessage = "Failed to write the MLLP payload to the Socket's OutputStream";
-            if (isAcknowledgementWriter()) {
-                throw new MllpAcknowledgementDeliveryException(errorMessage, hl7MessageBytes, hl7AcknowledgementBytes);
-            } else {
-                throw new MllpWriteException(errorMessage, hl7MessageBytes);
-            }
-        } finally {
-            outputBuffer.reset();
-        }
-
-    }
-}
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/impl/MllpSocketReader.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/impl/MllpSocketReader.java
deleted file mode 100644
index d0c78e0..0000000
--- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/impl/MllpSocketReader.java
+++ /dev/null
@@ -1,290 +0,0 @@
-/**
- * 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.impl;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.Socket;
-import java.net.SocketException;
-import java.net.SocketTimeoutException;
-
-import org.apache.camel.component.mllp.MllpAcknowledgementTimeoutException;
-import org.apache.camel.component.mllp.MllpComponent;
-import org.apache.camel.component.mllp.MllpException;
-import org.apache.camel.component.mllp.MllpReceiveAcknowledgementException;
-import org.apache.camel.component.mllp.MllpReceiveException;
-import org.apache.camel.component.mllp.MllpTimeoutException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import static org.apache.camel.component.mllp.MllpEndpoint.START_OF_BLOCK;
-
-public class MllpSocketReader {
-
-    final Socket socket;
-    final int receiveTimeout;
-    final int readTimeout;
-    final boolean acknowledgementReader;
-    Logger log = LoggerFactory.getLogger(this.getClass());
-    byte[] receiveBuffer;
-    ByteArrayOutputStream readAdditionalStream;
-
-    public MllpSocketReader(Socket socket, int receiveTimeout, int readTimeout, boolean acknowledgementReader) {
-        this.socket = socket;
-        this.receiveTimeout = receiveTimeout;
-        this.readTimeout = readTimeout;
-        this.acknowledgementReader = acknowledgementReader;
-        try {
-            receiveBuffer = new byte[socket.getReceiveBufferSize()];
-        } catch (SocketException socketEx) {
-            throw new IllegalStateException("Cannot retrieve the value of SO_RCVBUF from the Socket", socketEx);
-        }
-    }
-
-    public byte[] readEnvelopedPayload() throws MllpException {
-        return readEnvelopedPayload(null, null);
-    }
-
-    public byte[] readEnvelopedPayload(byte[] hl7MessageBytes) throws MllpException {
-        return readEnvelopedPayload(null, hl7MessageBytes);
-    }
-
-    public byte[] readEnvelopedPayload(Integer initialByte) throws MllpException {
-        return readEnvelopedPayload(initialByte, null);
-    }
-
-    protected byte[] readEnvelopedPayload(Integer initialByte, byte[] hl7MessageBytes) throws MllpException {
-        byte[] answer = null;
-
-        MllpSocketUtil.setSoTimeout(socket, receiveTimeout, log, "Preparing to receive payload");
-
-        InputStream socketInputStream = null;
-        try {
-            socketInputStream = socket.getInputStream();
-        } catch (IOException ioEx) {
-            final String errorMessage = "Failed to retrieve the InputStream from the Socket";
-            resetConnection(errorMessage);
-            throw isAcknowledgementReader()
-                    ? new MllpReceiveAcknowledgementException(errorMessage, hl7MessageBytes, ioEx)
-                    : new MllpReceiveException(errorMessage, ioEx);
-        }
-
-        // Read the acknowledgment - hopefully in one shot
-        int readCount;
-        int startPosition = (initialByte != null && initialByte == START_OF_BLOCK) ? 0 : -1;
-        do { // Read from the socket until the beginning of a MLLP payload is found or a timeout occurs
-            try {
-                readCount = socketInputStream.read(receiveBuffer);
-                if (readCount == -1) {
-                    String errorMessage = "END_OF_STREAM encountered while attempting to receive payload - was Socket closed?";
-                    resetConnection(errorMessage);
-                    throw isAcknowledgementReader()
-                            ? new MllpReceiveAcknowledgementException(errorMessage, hl7MessageBytes)
-                            : new MllpReceiveException(errorMessage);
-                } else if (log.isTraceEnabled()) {
-                    log.trace("Received bytes: {}", MllpComponent.covertBytesToPrintFriendlyString(receiveBuffer, 0, readCount));
-                }
-            } catch (SocketTimeoutException timeoutEx) {
-                if (isAcknowledgementReader()) {
-                    throw new MllpAcknowledgementTimeoutException(hl7MessageBytes, timeoutEx);
-                } else {
-                    if (initialByte != null && initialByte == START_OF_BLOCK) {
-                        answer = new byte[1];
-                        answer[0] = initialByte.byteValue();
-                        throw new MllpTimeoutException(answer, timeoutEx);
-                    }
-
-                    return null;
-                }
-            } catch (IOException ioEx) {
-                String errorMessage = "Error receiving payload";
-                log.error(errorMessage, ioEx);
-                resetConnection(errorMessage);
-                throw isAcknowledgementReader()
-                        ? new MllpReceiveAcknowledgementException(errorMessage, hl7MessageBytes, ioEx)
-                        : new MllpReceiveException(errorMessage, ioEx);
-            }
-
-            if (readCount > 0) {  // If some data was read, make sure we found the beginning of the message
-                if (initialByte != null && initialByte == START_OF_BLOCK) {
-                    startPosition = 0;
-                } else {
-                    int startOfBlock = MllpSocketUtil.findStartOfBlock(receiveBuffer, readCount);
-                    startPosition = (startOfBlock == -1) ? -1 : startOfBlock + 1;
-                }
-                if (startPosition > 1) {
-                    // Some out-of-band data was received - log it
-                    final String format = "Ignoring {} out-of-band bytes received before the beginning of the payload";
-                    int length = readCount - startPosition - 1;
-                    if (MllpComponent.isLogPhi()) {
-                        log.warn(format + ": {}", length, MllpComponent.covertBytesToPrintFriendlyString(receiveBuffer, 0, length));
-                    } else {
-                        log.warn(format, length);
-                    }
-                }
-            }
-        } while (startPosition == -1);
-
-        // Check to see if the payload is complete
-        int endPosition = MllpSocketUtil.findEndOfMessage(receiveBuffer, readCount);
-
-        if (endPosition != -1) {
-            // We have a complete payload - build the result without delimiters
-            if (endPosition < readCount - 3) {
-                // Some out-of-band data was received - log it
-                final String format = "Ignoring {} out-of-band bytes received after the end of the payload";
-                int length = readCount - endPosition - 2;
-                if (MllpComponent.isLogPhi()) {
-                    log.warn(format + ": {}", length, MllpComponent.covertBytesToPrintFriendlyString(receiveBuffer, endPosition + 1, length));
-                } else {
-                    log.warn(format, length);
-                }
-            }
-
-            // Build the answer
-            int length = endPosition - startPosition;
-            answer = new byte[length];
-            System.arraycopy(receiveBuffer, startPosition, answer, 0, length);
-        } else {
-            // The payload is incomplete - read it all before returning
-
-            // Write the data already received to the overflow stream, without the beginning delimiters
-            getReadAdditionalStream().reset();
-            readAdditionalStream.write(receiveBuffer, startPosition, readCount - startPosition);
-
-            // We've already received some data, so switch to the read timeout
-            MllpSocketUtil.setSoTimeout(socket, readTimeout, log, "Preparing to continue reading payload");
-
-            // Now the current data is in the overflow stream, continue reading until the end of the payload is found or a timeout occurs
-            endPosition = -1;
-            do { // Read from the socket until the end of the MLLP payload is found or a timeout occurs
-                try {
-                    readCount = socketInputStream.read(receiveBuffer);
-                    if (readCount == -1) {
-                        String errorMessage = "END_OF_STREAM encountered while attempting to read the end of the payload - Socket was closed or reset";
-                        resetConnection(errorMessage);
-                        byte[] partialPayload = (readAdditionalStream.size() > 0) ? readAdditionalStream.toByteArray() : null;
-                        throw isAcknowledgementReader()
-                                ? new MllpReceiveAcknowledgementException(errorMessage, hl7MessageBytes, partialPayload)
-                                : new MllpReceiveException(errorMessage, partialPayload);
-                    } else if (log.isTraceEnabled()) {
-                        log.trace("Read additional bytes: {}", MllpComponent.covertBytesToPrintFriendlyString(receiveBuffer, 0, readCount));
-                    }
-                } catch (SocketTimeoutException timeoutEx) {
-                    String errorMessage = "Timeout reading the end of the payload";
-                    resetConnection(errorMessage);
-                    byte[] partialPayload = (readAdditionalStream.size() > 0) ? readAdditionalStream.toByteArray() : null;
-                    throw isAcknowledgementReader()
-                            ? new MllpAcknowledgementTimeoutException(errorMessage, hl7MessageBytes, partialPayload, timeoutEx)
-                            : new MllpTimeoutException(errorMessage, partialPayload, timeoutEx);
-                } catch (IOException ioEx) {
-                    String errorMessage = "Error reading  the end of the payload";
-                    resetConnection(errorMessage);
-                    log.error(errorMessage);
-                    byte[] partialPayload = (readAdditionalStream.size() > 0) ? readAdditionalStream.toByteArray() : null;
-                    throw isAcknowledgementReader()
-                            ? new MllpReceiveAcknowledgementException(errorMessage, hl7MessageBytes, partialPayload, ioEx)
-                            : new MllpReceiveException(errorMessage, partialPayload, ioEx);
-                }
-                if (readCount > 0) {  // If some data was read, make sure we found the end of the message
-                    endPosition = MllpSocketUtil.findEndOfMessage(receiveBuffer, readCount);
-                    if (endPosition != -1) {
-                        if (endPosition < readCount - 2) {
-                            final String format = "Ignoring {} out-of-band bytes after the end of the payload";
-                            int length = readCount - endPosition - 2;
-                            if (MllpComponent.isLogPhi()) {
-                                log.warn(format + ": {}", length, MllpComponent.covertBytesToPrintFriendlyString(receiveBuffer, endPosition + 2, length));
-                            } else {
-                                log.warn(format, length);
-                            }
-                        }
-                        readAdditionalStream.write(receiveBuffer, 0, endPosition);
-                    } else {
-                        readAdditionalStream.write(receiveBuffer, 0, readCount);
-                    }
-                }
-            } while (endPosition == -1);
-
-            // All available data has been read - return the data
-            answer = readAdditionalStream.toByteArray();
-        }
-
-        // Check to see if there is any more data available
-        int availableCount;
-        do {
-            try {
-                availableCount = socketInputStream.available();
-            } catch (IOException ioEx) {
-                log.warn("Ignoring IOException encountered while checking for additional available trailing bytes", ioEx);
-                break;
-            }
-            if (availableCount > 0) { // if data is available, eat it
-                try {
-                    readCount = socketInputStream.read(receiveBuffer);
-                    final String format = "Ignoring {} out-of-band bytes trailing after the end of the payload";
-                    if (MllpComponent.isLogPhi()) {
-                        log.warn(format + ": {}", readCount, MllpComponent.covertBytesToPrintFriendlyString(receiveBuffer, 0, readCount));
-                    } else {
-                        log.warn(format, readCount);
-                    }
-                } catch (IOException ioEx) {
-                    log.warn(String.format("Ignoring IOException encountered while attempting to read %d bytes of trailing data", availableCount), ioEx);
-                    break;
-                }
-            }
-        } while (availableCount != 0);
-
-        return answer;
-    }
-
-    public void closeConnection(String reasonMessage) {
-        MllpSocketUtil.close(socket, log, reasonMessage);
-    }
-
-    public void resetConnection(String reasonMessage) {
-        MllpSocketUtil.reset(socket, log, reasonMessage);
-    }
-
-    public Socket getSocket() {
-        return socket;
-    }
-
-    public int getReceiveTimeout() {
-        return receiveTimeout;
-    }
-
-    public int getReadTimeout() {
-        return readTimeout;
-    }
-
-    public byte[] getReceiveBuffer() {
-        return receiveBuffer;
-    }
-
-    public boolean isAcknowledgementReader() {
-        return acknowledgementReader;
-    }
-
-    public ByteArrayOutputStream getReadAdditionalStream() {
-        if (readAdditionalStream == null) {
-            readAdditionalStream = new ByteArrayOutputStream(receiveBuffer.length);
-        }
-
-        return readAdditionalStream;
-    }
-}
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/impl/MllpSocketUtil.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/impl/MllpSocketUtil.java
deleted file mode 100644
index 0e3f07e..0000000
--- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/impl/MllpSocketUtil.java
+++ /dev/null
@@ -1,231 +0,0 @@
-/**
- * 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.impl;
-
-import java.io.IOException;
-import java.net.Socket;
-import java.net.SocketAddress;
-import java.net.SocketException;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import static org.apache.camel.component.mllp.MllpEndpoint.END_OF_BLOCK;
-import static org.apache.camel.component.mllp.MllpEndpoint.END_OF_DATA;
-import static org.apache.camel.component.mllp.MllpEndpoint.START_OF_BLOCK;
-
-public final class MllpSocketUtil {
-
-    private static final Logger LOG = LoggerFactory.getLogger(MllpSocketUtil.class);
-
-    private MllpSocketUtil() {
-    }
-
-    public static void setSoTimeout(Socket socket, int timeout, Logger logger, String reasonMessage) {
-        if (logger != null && logger.isDebugEnabled()) {
-            final String format = "Setting SO_TIMEOUT to {} for connection {}";
-            if (reasonMessage != null && !reasonMessage.isEmpty()) {
-                logger.debug(format + "  Reason: {}", timeout, getAddressString(socket), reasonMessage);
-            } else {
-                logger.debug(format, timeout, getAddressString(socket));
-            }
-        }
-        try {
-            socket.setSoTimeout(timeout);
-        } catch (SocketException socketEx) {
-            if (logger != null) {
-                final String format = "Ignoring SocketException encountered setting SO_TIMEOUT to %d for connection %s.";
-                if (reasonMessage != null && !reasonMessage.isEmpty()) {
-                    logger.warn(String.format(format + "  Reason: %s", timeout, getAddressString(socket), reasonMessage), socketEx);
-                } else {
-                    logger.warn(String.format(format, timeout, getAddressString(socket)), socketEx);
-                }
-            }
-        }
-    }
-
-    public static void close(Socket socket, Logger logger, String reasonMessage) {
-        if (socket != null && socket.isConnected() && !socket.isClosed()) {
-            final String format = "Closing connection {}";
-
-            String address = getAddressString(socket);
-
-            if (logger != null) {
-                if (reasonMessage != null && !reasonMessage.isEmpty()) {
-                    logger.warn(format + ".  Reason: {}", address, reasonMessage);
-                } else {
-                    logger.warn(format, address);
-                }
-            }
-
-            if (!socket.isInputShutdown()) {
-                try {
-                    socket.shutdownInput();
-                } catch (Exception ex) {
-                    String logMessage = String.format("Ignoring Exception encountered shutting down the input stream on the client socket %s", address);
-                    if (logger != null) {
-                        logger.warn(logMessage, ex);
-                    } else {
-                        LOG.warn(logMessage, ex);
-                    }
-                }
-            }
-
-            if (!socket.isOutputShutdown()) {
-                try {
-                    socket.shutdownOutput();
-                } catch (Exception ex) {
-                    String logMessage = String.format("Ignoring Exception encountered shutting down the output stream on the client socket %s", address);
-                    if (logger != null) {
-                        logger.warn(logMessage, ex);
-                    } else {
-                        LOG.warn(logMessage, ex);
-                    }
-                }
-            }
-
-            try {
-                socket.close();
-            } catch (IOException ioEx) {
-                String logMessage = String.format("Ignoring IOException encountered while closing connection %s", address);
-                if (logger != null) {
-                    logger.warn(logMessage, ioEx);
-                } else {
-                    LOG.warn(logMessage, ioEx);
-                }
-            }
-        }
-    }
-
-    public static void reset(Socket socket, Logger logger, String reasonMessage) {
-        if (socket != null && socket.isConnected() && !socket.isClosed()) {
-            final String format = "Resetting connection {}";
-
-            String address = getAddressString(socket);
-
-            if (logger != null) {
-                if (reasonMessage != null && !reasonMessage.isEmpty()) {
-                    logger.warn(format + ".  Reason: {}", address, reasonMessage);
-                } else {
-                    logger.warn(format, address);
-                }
-            }
-
-            try {
-                socket.setSoLinger(true, 0);
-            } catch (SocketException socketEx) {
-                String logMessage = String.format("Ignoring SocketException encountered setting SO_LINGER in preparation for resetting connection %s", address);
-                if (logger != null) {
-                    logger.warn(logMessage, socketEx);
-                } else {
-                    LOG.warn(logMessage, socketEx);
-                }
-            }
-
-            try {
-                socket.close();
-            } catch (IOException ioEx) {
-                String logMessage = String.format("Ignoring IOException encountered while resetting connection %s", address);
-                if (logger != null) {
-                    logger.warn(logMessage, ioEx);
-                } else {
-                    LOG.warn(logMessage, ioEx);
-                }
-            }
-        }
-    }
-
-    public static String getAddressString(Socket socket) {
-        String localAddressString = "null";
-        String remoteAddressString = "null";
-
-        if (socket != null) {
-            SocketAddress localSocketAddress = socket.getLocalSocketAddress();
-            if (localSocketAddress != null) {
-                localAddressString = localSocketAddress.toString();
-            }
-
-            SocketAddress remoteSocketAddress = socket.getRemoteSocketAddress();
-            if (remoteSocketAddress != null) {
-                remoteAddressString = remoteSocketAddress.toString();
-            }
-        }
-
-        return String.format("%s -> %s", localAddressString, remoteAddressString);
-    }
-
-    public static int findStartOfBlock(byte[] payload) {
-        if (payload != null) {
-            return findStartOfBlock(payload, payload.length);
-        }
-
-        return -1;
-    }
-
-    /**
-     * Find the beginning of the HL7 Payload
-     * <p>
-     * Searches the payload from the beginning, looking for the START_OF_BLOCK character.
-     *
-     * @param payload the payload to check
-     * @param length  the current valid length of the receive buffer
-     * @return the index of the START_OF_BLOCK, or -1 if not found
-     */
-    public static int findStartOfBlock(byte[] payload, int length) {
-        if (payload != null && length >= 0) {
-            for (int i = 0; i < Math.min(length, payload.length); ++i) {
-                if (payload[i] == START_OF_BLOCK) {
-                    return i;
-                }
-            }
-        }
-
-        return -1;
-    }
-
-    public static int findEndOfMessage(byte[] payload) {
-        if (payload != null) {
-            return findEndOfMessage(payload, payload.length);
-        }
-
-        return -1;
-    }
-
-    /**
-     * Find the end of the HL7 Payload
-     * <p>
-     * Searches the payload from the end, looking for the [END_OF_BLOCK, END_OF_DATA] characters.
-     *
-     * @param payload the payload to check
-     * @param length  the current valid length of the receive buffer
-     * @return the index of the END_OF_BLOCK character that terminates the message, or -1 if not found
-     */
-    public static int findEndOfMessage(byte[] payload, int length) {
-        if (payload != null && length >= 0) {
-            for (int i = Math.min(length, payload.length) - 1; i > 0; --i) {
-                if (payload[i] == END_OF_DATA) {
-                    if (payload[i - 1] == END_OF_BLOCK) {
-                        return i - 1;
-                    }
-                }
-            }
-        }
-
-        return -1;
-    }
-
-}
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/impl/MllpSocketWriter.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/impl/MllpSocketWriter.java
deleted file mode 100644
index 3f9310d..0000000
--- a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/impl/MllpSocketWriter.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/**
- * 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.impl;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.net.Socket;
-
-import org.apache.camel.component.mllp.MllpAcknowledgementDeliveryException;
-import org.apache.camel.component.mllp.MllpException;
-import org.apache.camel.component.mllp.MllpWriteException;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import static org.apache.camel.component.mllp.MllpEndpoint.END_OF_BLOCK;
-import static org.apache.camel.component.mllp.MllpEndpoint.END_OF_DATA;
-import static org.apache.camel.component.mllp.MllpEndpoint.START_OF_BLOCK;
-
-public class MllpSocketWriter {
-
-    static final byte[] PAYLOAD_TERMINATOR;
-
-    static {
-        PAYLOAD_TERMINATOR = new byte[2];
-        PAYLOAD_TERMINATOR[0] = END_OF_BLOCK;
-        PAYLOAD_TERMINATOR[1] = END_OF_DATA;
-    }
-
-    final Socket socket;
-    final boolean acknowledgementWriter;
-
-    Logger log = LoggerFactory.getLogger(this.getClass());
-
-    public MllpSocketWriter(Socket socket, boolean acknowledgementWriter) {
-        this.socket = socket;
-        this.acknowledgementWriter = acknowledgementWriter;
-    }
-
-    public void writeEnvelopedPayload(byte[] hl7MessageBytes, byte[] hl7AcknowledgementBytes) throws MllpException {
-        if (socket == null) {
-            final String errorMessage = "Socket is null";
-            if (isAcknowledgementWriter()) {
-                throw new MllpAcknowledgementDeliveryException(errorMessage, hl7MessageBytes, hl7AcknowledgementBytes);
-            } else {
-                throw new MllpWriteException(errorMessage, hl7MessageBytes);
-            }
-        } else if (!socket.isConnected()) {
-            final String errorMessage = "Socket is not connected";
-            if (isAcknowledgementWriter()) {
-                throw new MllpAcknowledgementDeliveryException(errorMessage, hl7MessageBytes, hl7AcknowledgementBytes);
-            } else {
-                throw new MllpWriteException(errorMessage, hl7MessageBytes);
-            }
-        } else if (socket.isClosed()) {
-            final String errorMessage = "Socket is closed";
-            if (isAcknowledgementWriter()) {
-                throw new MllpAcknowledgementDeliveryException(errorMessage, hl7MessageBytes, hl7AcknowledgementBytes);
-            } else {
-                throw new MllpWriteException(errorMessage, hl7MessageBytes);
-            }
-        }
-
-        OutputStream socketOutputStream = null;
-        try {
-            socketOutputStream = socket.getOutputStream();
-        } catch (IOException e) {
-            final String errorMessage = "Failed to retrieve the OutputStream from the Socket";
-            if (isAcknowledgementWriter()) {
-                throw new MllpAcknowledgementDeliveryException(errorMessage, hl7MessageBytes, hl7AcknowledgementBytes);
-            } else {
-                throw new MllpWriteException(errorMessage, hl7MessageBytes, hl7AcknowledgementBytes);
-            }
-        }
-
-        try {
-            socketOutputStream.write(START_OF_BLOCK);
-        } catch (IOException e) {
-            final String errorMessage = "Failed to write the START_OF_BLOCK to the Socket's OutputStream";
-            if (isAcknowledgementWriter()) {
-                throw new MllpAcknowledgementDeliveryException(errorMessage, hl7MessageBytes, hl7AcknowledgementBytes);
-            } else {
-                throw new MllpWriteException(errorMessage, hl7MessageBytes, hl7AcknowledgementBytes);
-            }
-        }
-
-        if (isAcknowledgementWriter()) {
-            if (hl7AcknowledgementBytes == null) {
-                log.warn("HL7 Acknowledgement payload is null - sending empty MLLP payload");
-            } else if (hl7AcknowledgementBytes.length <= 0) {
-                log.warn("HL7 Acknowledgement payload is empty - sending empty MLLP payload");
-            } else {
-                try {
-                    socketOutputStream.write(hl7AcknowledgementBytes);
-                } catch (IOException ioEx) {
-                    throw new MllpAcknowledgementDeliveryException("Failed to write the HL7 Acknowledgement payload to the Socket's OutputStream", hl7MessageBytes, hl7AcknowledgementBytes, ioEx);
-                }
-            }
-        } else {
-            if (hl7MessageBytes == null) {
-                log.warn("HL7 Message payload is null - sending empty MLLP payload");
-            } else if (hl7MessageBytes.length <= 0) {
-                log.warn("HL7 Message payload is empty - sending empty MLLP payload");
-            } else {
-                try {
-                    socketOutputStream.write(hl7MessageBytes);
-                } catch (IOException ioEx) {
-                    throw new MllpWriteException("Failed to write the HL7 Message payload to the Socket's OutputStream", hl7MessageBytes, hl7AcknowledgementBytes, ioEx);
-                }
-            }
-        }
-
-        try {
-            socketOutputStream.write(PAYLOAD_TERMINATOR);
-            socketOutputStream.flush();
-        } catch (IOException e) {
-            final String errorMessage = "Failed to write the END_OF_BLOCK and END_OF_DATA to the Socket's OutputStream";
-            if (isAcknowledgementWriter()) {
-                throw new MllpAcknowledgementDeliveryException(errorMessage, hl7MessageBytes, hl7AcknowledgementBytes);
-            } else {
-                throw new MllpWriteException(errorMessage, hl7MessageBytes, hl7AcknowledgementBytes);
-            }
-        }
-
-    }
-
-    public boolean isAcknowledgementWriter() {
-        return acknowledgementWriter;
-    }
-}
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
new file mode 100644
index 0000000..40390f1
--- /dev/null
+++ b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/internal/Hl7Util.java
@@ -0,0 +1,336 @@
+/**
+ * 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.internal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.component.mllp.MllpComponent;
+import org.apache.camel.component.mllp.MllpProtocolConstants;
+import org.apache.camel.processor.mllp.Hl7AcknowledgementGenerationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class Hl7Util {
+    public static final String START_OF_BLOCK_REPLACEMENT_VALUE = "<VT>";      // VT (vertical tab)        - decimal 11, octal 013
+    public static final String END_OF_BLOCK_REPLACEMENT_VALUE = "<FS>";        // FS (file separator)      - decimal 28, octal 034
+    public static final String SEGMENT_DELIMITER_REPLACEMENT_VALUE = "<CR>";   // CR (carriage return)     - decimal 13, octal 015
+    public static final String MESSAGE_TERMINATOR_REPLACEMENT_VALUE = "<LF>";  // LF (line feed, new line) - decimal 10, octal 012
+    // Some other non-printable characters
+    public static final String TAB_REPLACEMENT_VALUE = "<TAB>";      // TAB (tab)        - decimal 9, octal 011
+    public static final String BACKSPACE_REPLACEMENT_VALUE = "<BS>";      // BS (backspace)        - decimal 8, octal 010
+    public static final String FORMFEED_REPLACEMENT_VALUE = "<FF>";      // FF (tab)        - decimal 12, octal 014
+
+    public static final String NULL_REPLACEMENT_VALUE = "<null>";
+    public static final String EMPTY_REPLACEMENT_VALUE = "<>";
+
+    static final int STRING_BUFFER_PAD_SIZE = 100;
+
+    static final Logger LOG = LoggerFactory.getLogger(Hl7Util.class);
+
+    private Hl7Util() {
+        //utility class, never constructed
+    }
+
+    public static String generateInvalidPayloadExceptionMessage(final byte[] hl7Bytes) {
+        if (hl7Bytes == null) {
+            return "HL7 payload is null";
+        }
+
+        return generateInvalidPayloadExceptionMessage(hl7Bytes, hl7Bytes.length);
+    }
+
+    /**
+     * Verifies that the HL7 payload array <p> The MLLP protocol does not allow embedded START_OF_BLOCK or END_OF_BLOCK characters.  The END_OF_DATA character is allowed (and expected) because it is
+     * also the segment delimiter for an HL7 message
+     *
+     * @param hl7Bytes the HL7 payload to validate
+     *
+     * @return If the payload is invalid, an error message suitable for inclusion in an exception is returned.  If the payload is valid, null is returned;
+     */
+    public static String generateInvalidPayloadExceptionMessage(final byte[] hl7Bytes, final int length) {
+        if (hl7Bytes == null) {
+            return "HL7 payload is null";
+        }
+
+        if (hl7Bytes.length <= 0) {
+            return "HL7 payload is empty";
+        }
+
+        if (length > hl7Bytes.length) {
+            LOG.warn("The length specified for the HL7 payload array <{}> is greater than the actual length of the array <{}> - only validating {} bytes", length, hl7Bytes.length, hl7Bytes.length);
+        }
+
+        if (hl7Bytes.length < 3 || hl7Bytes[0] != 'M' || hl7Bytes[1] != 'S' || hl7Bytes[2] != 'H') {
+            return String.format("The first segment of the HL7 payload {%s} is not an MSH segment", new String(hl7Bytes, 0, Math.min(3, hl7Bytes.length)));
+        }
+
+        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);
+        }
+
+        for (int i = 0; i < validationLength; ++i) {
+            switch (hl7Bytes[i]) {
+            case MllpProtocolConstants.START_OF_BLOCK:
+                return String.format("HL7 payload contains an embedded START_OF_BLOCK {%#x, ASCII <VT>} at index %d", hl7Bytes[i], i);
+            case MllpProtocolConstants.END_OF_BLOCK:
+                return String.format("HL7 payload contains an embedded END_OF_BLOCK {%#x, ASCII <FS>} at index %d", hl7Bytes[i], i);
+            default:
+                // continue on
+            }
+        }
+
+        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");
+        }
+
+        final byte fieldSeparator = hl7MessageBytes[3];
+        final byte componentSeparator = hl7MessageBytes[4];
+
+        List<Integer> fieldSeparatorIndexes = new ArrayList<>(10);  // We need at least 10 fields to create the acknowledgment
+
+        // 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;
+            }
+        }
+
+        if (-1 == endOfMSH) {
+            throw new Hl7AcknowledgementGenerationException("Failed to find the end of the  MSH Segment while attempting to generate response", hl7MessageBytes);
+        }
+
+        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);
+        }
+
+        // Start building the MLLP Envelope
+        mllpSocketBuffer.openMllpEnvelope();
+
+        // 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
+        // Need to generate the correct MSH-9
+        mllpSocketBuffer.write(fieldSeparator);
+        mllpSocketBuffer.write("ACK".getBytes(), 0, 3); // MSH-9.1
+        int msh92start = -1;
+        for (int j = fieldSeparatorIndexes.get(7) + 1; j < fieldSeparatorIndexes.get(8); ++j) {
+            if (componentSeparator == hl7MessageBytes[j]) {
+                msh92start = j;
+                break;
+            }
+        }
+
+        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, fieldSeparatorIndexes.get(8), endOfMSH - fieldSeparatorIndexes.get(8)); // MSH-10 through the end of the MSH
+        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
+        mllpSocketBuffer.write(MllpProtocolConstants.SEGMENT_DELIMITER);
+
+        // Close the MLLP Envelope
+        mllpSocketBuffer.write(MllpProtocolConstants.PAYLOAD_TERMINATOR);
+
+        return;
+    }
+
+    public static String convertToPrintFriendlyString(String phiString) {
+        if (null == phiString) {
+            return NULL_REPLACEMENT_VALUE;
+        } else if (phiString.length() == 0) {
+            return EMPTY_REPLACEMENT_VALUE;
+        }
+
+        int logPhiMaxBytes = MllpComponent.getLogPhiMaxBytes();
+        int conversionLength = (logPhiMaxBytes > 0) ? Integer.min(phiString.length(), logPhiMaxBytes) : phiString.length();
+
+        StringBuilder builder = new StringBuilder(conversionLength + STRING_BUFFER_PAD_SIZE);
+
+        for (int i = 0; i < conversionLength; ++i) {
+            appendCharacterAsPrintFriendlyString(builder, phiString.charAt(i));
+        }
+
+        return builder.toString();
+    }
+
+    public static String convertToPrintFriendlyString(byte[] phiBytes) {
+        return bytesToPrintFriendlyStringBuilder(phiBytes).toString();
+    }
+
+    /**
+     * Convert a PHI byte[] to a String, replacing specific non-printable characters with readable strings.
+     *
+     * NOTE: this conversion uses the default character set, so not all characters my convert correctly.
+     *
+     * @param phiBytes      the PHI byte[] to log
+     * @param startPosition the starting position/index of the data
+     * @param endPosition   the ending position/index of the data - will not be included in String
+     *
+     * @return a String representation of the byte[]
+     */
+    public static String convertToPrintFriendlyString(byte[] phiBytes, int startPosition, int endPosition) {
+        return bytesToPrintFriendlyStringBuilder(phiBytes, startPosition, endPosition).toString();
+    }
+
+    /**
+     * Convert a PHI byte[] to a StringBuilder, replacing specific non-printable characters with readable strings.
+     *
+     * NOTE: this conversion uses the default character set, so not all characters my convert correctly.
+     *
+     * @param phiBytes the PHI byte[] to log
+     *
+     * @return
+     */
+    public static StringBuilder bytesToPrintFriendlyStringBuilder(byte[] phiBytes) {
+        return bytesToPrintFriendlyStringBuilder(phiBytes, 0, phiBytes != null ? phiBytes.length : -1);
+    }
+
+    /**
+     * Convert a PHI byte[] to a StringBuilder, replacing specific non-printable characters with readable strings.
+     *
+     * NOTE: this conversion uses the default character set, so not all characters my convert correctly.
+     *
+     * @param phiBytes      the PHI byte[] to log
+     * @param startPosition the starting position/index of the data
+     * @param endPosition   the ending position/index of the data - will not be included in StringBuilder
+     *
+     * @return a String representation of the byte[]
+     */
+    public static StringBuilder bytesToPrintFriendlyStringBuilder(byte[] phiBytes, int startPosition, int endPosition) {
+        StringBuilder answer = new StringBuilder();
+
+        appendBytesAsPrintFriendlyString(answer, phiBytes, startPosition, endPosition);
+
+        return answer;
+    }
+
+    public static void appendBytesAsPrintFriendlyString(StringBuilder builder, byte[] phiBytes) {
+        appendBytesAsPrintFriendlyString(builder, phiBytes, 0, phiBytes != null ? phiBytes.length : 0);
+    }
+
+    /**
+     * Append a PHI byte[] to a StringBuilder, replacing specific non-printable characters with readable strings.
+     *
+     * NOTE: this conversion uses the default character set, so not all characters my convert correctly.
+     *
+     * @param phiBytes      the PHI byte[] to log
+     * @param startPosition the starting position/index of the data
+     * @param endPosition   the ending position/index of the data - will not be included in String
+     */
+    public static void appendBytesAsPrintFriendlyString(StringBuilder builder, byte[] phiBytes, int startPosition, int endPosition) {
+        if (builder == null) {
+            throw new IllegalArgumentException("StringBuilder cannot be null");
+        }
+
+        if (null == phiBytes) {
+            builder.append(NULL_REPLACEMENT_VALUE);
+        } else if (phiBytes.length == 0) {
+            builder.append(EMPTY_REPLACEMENT_VALUE);
+        } else if (startPosition <= endPosition) {
+            if (startPosition < 0) {
+                startPosition = 0;
+            }
+
+            if (startPosition < phiBytes.length) {
+                if (endPosition >= -1) {
+                    if (endPosition == -1 || endPosition >= phiBytes.length) {
+                        endPosition = phiBytes.length;
+                    }
+
+                    int length = endPosition - startPosition;
+                    if (length > 0) {
+                        int logPhiMaxBytes = MllpComponent.getLogPhiMaxBytes();
+                        int conversionLength = (logPhiMaxBytes > 0) ? Integer.min(length, logPhiMaxBytes) : length;
+                        if (builder.capacity() - builder.length() < conversionLength + STRING_BUFFER_PAD_SIZE) {
+                            builder.ensureCapacity(builder.length() + conversionLength + STRING_BUFFER_PAD_SIZE);
+                        }
+                        for (int i = 0; i < conversionLength; ++i) {
+                            appendCharacterAsPrintFriendlyString(builder, (char) phiBytes[startPosition + i]);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    static void appendCharacterAsPrintFriendlyString(StringBuilder builder, char c) {
+        switch (c) {
+        case MllpProtocolConstants.START_OF_BLOCK:
+            builder.append(START_OF_BLOCK_REPLACEMENT_VALUE);
+            break;
+        case MllpProtocolConstants.END_OF_BLOCK:
+            builder.append(END_OF_BLOCK_REPLACEMENT_VALUE);
+            break;
+        case MllpProtocolConstants.SEGMENT_DELIMITER:
+            builder.append(SEGMENT_DELIMITER_REPLACEMENT_VALUE);
+            break;
+        case MllpProtocolConstants.MESSAGE_TERMINATOR:
+            builder.append(MESSAGE_TERMINATOR_REPLACEMENT_VALUE);
+            break;
+        default:
+            builder.append(c);
+        }
+    }
+
+    public static String getCharacterAsPrintFriendlyString(char c) {
+        switch (c) {
+        case MllpProtocolConstants.START_OF_BLOCK:
+            return START_OF_BLOCK_REPLACEMENT_VALUE;
+        case MllpProtocolConstants.END_OF_BLOCK:
+            return END_OF_BLOCK_REPLACEMENT_VALUE;
+        case MllpProtocolConstants.SEGMENT_DELIMITER:
+            return SEGMENT_DELIMITER_REPLACEMENT_VALUE;
+        case MllpProtocolConstants.MESSAGE_TERMINATOR:
+            return MESSAGE_TERMINATOR_REPLACEMENT_VALUE;
+        case '\t':
+            return TAB_REPLACEMENT_VALUE;
+        case '\b':
+            return BACKSPACE_REPLACEMENT_VALUE;
+        case '\f':
+            return FORMFEED_REPLACEMENT_VALUE;
+        default:
+            return String.valueOf(c);
+        }
+    }
+}
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/internal/MllpSocketBuffer.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/internal/MllpSocketBuffer.java
new file mode 100644
index 0000000..8839045
--- /dev/null
+++ b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/internal/MllpSocketBuffer.java
@@ -0,0 +1,653 @@
+/**
+ * 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.internal;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.net.SocketTimeoutException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+import org.apache.camel.component.mllp.MllpEndpoint;
+import org.apache.camel.component.mllp.MllpProtocolConstants;
+import org.apache.camel.component.mllp.MllpSocketException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * An OutputStream modeled after the ByteArrayOutputStream specifically for MLLP operations.
+ */
+public class MllpSocketBuffer {
+    static final Charset DEFAULT_CHARSET = StandardCharsets.US_ASCII;
+    static final int MIN_BUFFER_SIZE = 2048;
+    static final int MAX_BUFFER_SIZE = 0x40000000;  // Approximately 1-GB
+
+    final Logger log = LoggerFactory.getLogger(this.getClass());
+    final MllpEndpoint endpoint;
+
+    byte buffer[];
+
+    int availableByteCount;
+
+    int startOfBlockIndex = -1;
+    int endOfBlockIndex = -1;
+
+    public MllpSocketBuffer(MllpEndpoint endpoint) {
+        if (endpoint == null) {
+            throw new IllegalArgumentException("MllpEndpoint cannot be null");
+        }
+        this.endpoint = endpoint;
+
+        buffer = new byte[MIN_BUFFER_SIZE];
+    }
+
+    public boolean isEndOfDataRequired() {
+        return endpoint.getConfiguration().isRequireEndOfData();
+    }
+
+    public boolean isEmpty() {
+        return (size() > 0) ? false : true;
+    }
+
+    public synchronized void write(int b) {
+        ensureCapacity(1);
+        buffer[availableByteCount] = (byte) b;
+
+        updateIndexes(b, 0);
+
+        availableByteCount += 1;
+    }
+
+
+    public void write(byte[] b) {
+        if (b != null && b.length > 0) {
+            this.write(b, 0, b.length);
+        }
+    }
+
+    public synchronized void write(byte[] sourceBytes, int offset, int writeCount) {
+        if (sourceBytes != null && sourceBytes.length > 0) {
+            if (offset < 0) {
+                throw new IndexOutOfBoundsException(
+                    String.format("offset <%d> is less than zero",
+                        offset));
+            }
+            if (offset > sourceBytes.length) {
+                throw new IndexOutOfBoundsException(
+                    String.format("offset <%d> is greater than write count <%d>",
+                        offset, writeCount));
+            }
+
+            if (writeCount < 0) {
+                throw new IndexOutOfBoundsException(
+                    String.format("write count <%d> is less than zero",
+                        writeCount));
+            }
+            if (writeCount > sourceBytes.length) {
+                throw new IndexOutOfBoundsException(
+                    String.format("write count <%d> is greater than length of the source byte[] <%d>",
+                        writeCount, sourceBytes.length));
+            }
+            if ((offset + writeCount) - sourceBytes.length > 0) {
+                throw new IndexOutOfBoundsException(
+                    String.format("offset <%d> plus write count <%d> is <%d> is greater than length <%d> of the source byte[]",
+                    offset, writeCount, offset + writeCount, sourceBytes.length));
+            }
+
+            ensureCapacity(writeCount);
+            System.arraycopy(sourceBytes, offset, buffer, availableByteCount, writeCount);
+
+            for (int i = offset; i < writeCount && (startOfBlockIndex < 0 || endOfBlockIndex < 0); ++i) {
+                updateIndexes(sourceBytes[i], i);
+            }
+
+            availableByteCount += writeCount;
+        }
+    }
+
+    public synchronized void openMllpEnvelope() {
+        reset();
+        write(MllpProtocolConstants.START_OF_BLOCK);
+    }
+
+    public synchronized void closeMllpEnvelope() {
+        write(MllpProtocolConstants.PAYLOAD_TERMINATOR);
+    }
+
+    public synchronized void setEnvelopedMessage(byte[] hl7Payload) {
+        setEnvelopedMessage(hl7Payload, 0, hl7Payload != null ? hl7Payload.length : 0);
+    }
+
+    public synchronized void setEnvelopedMessage(byte[] hl7Payload, int offset, int length) {
+        reset();
+
+        if (hl7Payload != null && hl7Payload.length > 0) {
+            if (hl7Payload[0] != MllpProtocolConstants.START_OF_BLOCK) {
+                write(MllpProtocolConstants.START_OF_BLOCK);
+            }
+
+            write(hl7Payload, offset, length);
+
+            if (!hasCompleteEnvelope()) {
+                write(MllpProtocolConstants.PAYLOAD_TERMINATOR);
+            }
+        } else {
+            write(MllpProtocolConstants.START_OF_BLOCK);
+            write(MllpProtocolConstants.PAYLOAD_TERMINATOR);
+        }
+    }
+
+    public synchronized void reset() {
+        if (availableByteCount > 0) {
+            // TODO: May be able to get rid of this
+            Arrays.fill(buffer, (byte) 0);
+        }
+
+        availableByteCount = 0;
+
+        startOfBlockIndex = -1;
+        endOfBlockIndex = -1;
+    }
+
+
+    public synchronized void readFrom(Socket socket) throws MllpSocketException, SocketTimeoutException {
+        log.trace("Entering readFrom ...");
+        if (socket != null && socket.isConnected() && !socket.isClosed()) {
+            ensureCapacity(MIN_BUFFER_SIZE);
+
+            try {
+                InputStream socketInputStream = socket.getInputStream();
+
+                socket.setSoTimeout(endpoint.getConfiguration().getReceiveTimeout());
+
+                readSocketInputStream(socketInputStream, socket);
+                if (!hasCompleteEnvelope()) {
+                    socket.setSoTimeout(endpoint.getConfiguration().getReadTimeout());
+
+                    while (!hasCompleteEnvelope()) {
+                        ensureCapacity(Math.max(MIN_BUFFER_SIZE, socketInputStream.available()));
+                        readSocketInputStream(socketInputStream, socket);
+                    }
+                }
+
+            } catch (SocketTimeoutException timeoutEx) {
+                throw timeoutEx;
+            } catch (IOException ioEx) {
+                final String exceptionMessage = "Exception encountered reading Socket";
+                resetSocket(socket, exceptionMessage);
+                throw new MllpSocketException(exceptionMessage, ioEx);
+            } finally {
+                if (size() > 0 && !hasCompleteEnvelope()) {
+                    if (!hasEndOfData() && hasEndOfBlock() && endOfBlockIndex < size() - 1) {
+                        log.warn("readFrom exiting with partial payload ", Hl7Util.convertToPrintFriendlyString(buffer, 0, size() - 1));
+                    }
+                }
+            }
+
+        } else {
+            log.warn("Socket is invalid - no data read");
+        }
+
+        log.trace("Exiting readFrom ...");
+    }
+
+    public synchronized void writeTo(Socket socket) throws MllpSocketException {
+        log.trace("Entering writeTo ...");
+        if (socket != null && socket.isConnected() && !socket.isClosed()) {
+            if (!isEmpty()) {
+                try {
+                    OutputStream socketOutputStream = socket.getOutputStream();
+                    if (hasStartOfBlock()) {
+                        if (hasEndOfData()) {
+                            socketOutputStream.write(buffer, startOfBlockIndex, endOfBlockIndex - startOfBlockIndex  + 2);
+                        } else if (hasEndOfBlock()) {
+                            socketOutputStream.write(buffer, startOfBlockIndex, endOfBlockIndex - startOfBlockIndex + 1);
+                            socketOutputStream.write(MllpProtocolConstants.END_OF_DATA);
+                        } else {
+                            socketOutputStream.write(buffer, startOfBlockIndex, availableByteCount - startOfBlockIndex);
+                            socketOutputStream.write(MllpProtocolConstants.PAYLOAD_TERMINATOR);
+                        }
+                    } else {
+                        socketOutputStream.write(MllpProtocolConstants.START_OF_BLOCK);
+                        socketOutputStream.write(buffer, 0, availableByteCount);
+                        socketOutputStream.write(MllpProtocolConstants.PAYLOAD_TERMINATOR);
+                    }
+                    socketOutputStream.flush();
+                } catch (IOException ioEx) {
+                    final String exceptionMessage = "Exception encountered writing Socket";
+                    resetSocket(socket, exceptionMessage);
+                    throw new MllpSocketException(exceptionMessage, ioEx);
+                }
+            } else {
+                log.warn("Ignoring call to writeTo(byte[] payload) - MLLP payload is null or empty");
+            }
+        } else {
+            log.warn("Socket is invalid - no data written");
+        }
+
+        log.trace("Exiting writeTo ...");
+    }
+
+    public synchronized byte toByteArray()[] {
+        if (availableByteCount > 0) {
+            return Arrays.copyOf(buffer, availableByteCount);
+        }
+
+        return null;
+    }
+
+    public synchronized byte toByteArrayAndReset()[] {
+        byte[] answer = toByteArray();
+
+        reset();
+
+        return answer;
+    }
+
+    @Override
+    public synchronized String toString() {
+        return toString(DEFAULT_CHARSET);
+    }
+
+    public synchronized String toString(Charset charset) {
+        if (availableByteCount > 0) {
+            return new String(buffer, 0, availableByteCount, charset);
+        }
+
+        return "";
+    }
+
+    public synchronized String toString(String charsetName) {
+        if (availableByteCount > 0) {
+            if (Charset.isSupported(charsetName)) {
+                Charset charset = Charset.forName(charsetName);
+                return toString(charset);
+            } else if (MllpProtocolConstants.MSH18_VALUES.containsKey(charsetName)) {
+                return toString(MllpProtocolConstants.MSH18_VALUES.get(charsetName));
+            } else {
+                return toString(DEFAULT_CHARSET);
+            }
+        }
+
+        return "";
+    }
+
+    /**
+     * Convert the entire contents of the buffer (including enveloping characters) to a print-friendly
+     * String representation.
+     *
+     * @return print-friendly String
+     */
+    public synchronized String toPrintFriendlyString() {
+        if (availableByteCount > 0) {
+            return Hl7Util.convertToPrintFriendlyString(buffer, 0, availableByteCount);
+        }
+
+        return "";
+    }
+
+    public synchronized String toHl7String() {
+        return this.toHl7String(StandardCharsets.US_ASCII.name());
+    }
+
+    public synchronized String toHl7String(String charsetName) {
+        String hl7String = null;
+
+        if (hasCompleteEnvelope()) {
+            int offset = hasStartOfBlock() ? startOfBlockIndex + 1 : 1;
+            int length = hasEndOfBlock() ? endOfBlockIndex - offset : availableByteCount - startOfBlockIndex - 1;
+            if (length > 0) {
+                try {
+                    hl7String = new String(buffer,
+                        offset,
+                        length,
+                        charsetName);
+                } catch (UnsupportedEncodingException unsupportedEncodingEx) {
+                    log.warn("Failed to create string using {} charset - falling back to default charset {}", charsetName, MllpProtocolConstants.DEFAULT_CHARSET);
+                    hl7String = new String(buffer, offset, length, MllpProtocolConstants.DEFAULT_CHARSET);
+                }
+            } else {
+                hl7String = "";
+            }
+        }
+
+        return hl7String;
+    }
+
+    /**
+     * Convert the enveloped contents of the buffer (excluding enveloping characters) to a print-friendly
+     * String representation.
+     *
+     * @return print-friendly String
+     */
+    public synchronized String toPrintFriendlyHl7String() {
+        if (hasCompleteEnvelope()) {
+            int startPosition = hasStartOfBlock() ? startOfBlockIndex + 1 : 1;
+            int endPosition = hasEndOfBlock() ? endOfBlockIndex : availableByteCount - 1;
+            return Hl7Util.convertToPrintFriendlyString(buffer, startPosition, endPosition);
+        }
+
+        return "";
+    }
+
+    public synchronized byte[] toMllpPayload() {
+        byte[] mllpPayload = null;
+
+        if (hasCompleteEnvelope()) {
+            int offset = hasStartOfBlock() ? startOfBlockIndex + 1 : 1;
+            int length = hasEndOfBlock() ? endOfBlockIndex - offset : availableByteCount - startOfBlockIndex - 1;
+
+            if (length > 0) {
+                mllpPayload = new byte[length];
+                System.arraycopy(buffer, offset, mllpPayload, 0, length);
+            } else {
+                mllpPayload = new byte[0];
+            }
+        }
+
+        return mllpPayload;
+    }
+
+    public synchronized int getMllpPayloadLength() {
+        int answer = -1;
+
+        if (hasCompleteEnvelope()) {
+            if (isEndOfDataRequired()) {
+                answer = endOfBlockIndex - startOfBlockIndex + 2;
+            } else {
+                answer = endOfBlockIndex - startOfBlockIndex + 2;
+            }
+        }
+
+        return answer;
+    }
+
+
+
+
+    public synchronized int getStartOfBlockIndex() {
+        return startOfBlockIndex;
+    }
+
+
+    public synchronized int getEndOfBlockIndex() {
+        return endOfBlockIndex;
+    }
+
+    public synchronized boolean hasCompleteEnvelope() {
+        if (hasStartOfBlock()) {
+            if (isEndOfDataRequired()) {
+                return hasEndOfData();
+            } else {
+                return hasEndOfBlock();
+            }
+        }
+
+        return false;
+    }
+
+    public synchronized boolean hasStartOfBlock() {
+        return (startOfBlockIndex >= 0) ? true : false;
+    }
+
+
+    public synchronized boolean hasEndOfBlock() {
+        return (endOfBlockIndex >= 0) ? true : false;
+    }
+
+    public synchronized boolean hasEndOfData() {
+        if (hasEndOfBlock()) {
+            int potentialEndOfDataIndex = endOfBlockIndex + 1;
+            if ((potentialEndOfDataIndex < availableByteCount) && (buffer[potentialEndOfDataIndex] == MllpProtocolConstants.END_OF_DATA)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public synchronized boolean hasOutOfBandData() {
+        return hasLeadingOutOfBandData() || hasTrailingOutOfBandData();
+    }
+
+    public synchronized boolean hasLeadingOutOfBandData() {
+        if (size() > 0) {
+            if (!hasStartOfBlock() || startOfBlockIndex > 0) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public synchronized boolean hasTrailingOutOfBandData() {
+        if (size() > 0) {
+            if (hasEndOfData()) {
+                if (endOfBlockIndex + 1 < size() - 1) {
+                    return true;
+                }
+            } else if (!isEndOfDataRequired()) {
+                if (hasEndOfBlock() && endOfBlockIndex < size() - 1) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    public synchronized byte[] getLeadingOutOfBandData() {
+        byte[] outOfBandData = null;
+
+        if (hasLeadingOutOfBandData()) {
+            outOfBandData = new byte[startOfBlockIndex == -1 ? availableByteCount : startOfBlockIndex];
+            System.arraycopy(buffer, 0, outOfBandData, 0, outOfBandData.length);
+        }
+
+        return outOfBandData;
+    }
+
+    public synchronized byte[] getTrailingOutOfBandData() {
+        byte[] outOfBandData = null;
+
+        if (hasTrailingOutOfBandData()) {
+            int offset = hasEndOfData() ? endOfBlockIndex + 2 : endOfBlockIndex + 1;
+            int length = size() - offset;
+            outOfBandData = new byte[length];
+            System.arraycopy(buffer, offset, outOfBandData, 0, length);
+        }
+
+        return outOfBandData;
+    }
+
+    public synchronized int size() {
+        return availableByteCount;
+    }
+
+    public synchronized int capacity() {
+        if (buffer != null) {
+            return buffer.length - availableByteCount;
+        }
+
+        return -1;
+    }
+
+    public synchronized int bufferSize() {
+        if (buffer != null) {
+            return buffer.length;
+        }
+
+        return -1;
+    }
+
+    /**
+     * Get the internal buffer.
+     *
+     * USE WITH CAUTION!!
+     *
+     * @return
+     */
+    public byte[] getBuffer() {
+        return buffer;
+    }
+
+    void ensureCapacity(int requiredAvailableCapacity) {
+        int currentAvailableCapacity = capacity();
+
+        if (requiredAvailableCapacity > currentAvailableCapacity) {
+            int requiredBufferSize = buffer.length + (requiredAvailableCapacity - currentAvailableCapacity);
+
+            if (buffer.length >= MAX_BUFFER_SIZE) {
+                final String exceptionMessageFormat = "Cannot increase the buffer size from <%d> to <%d>"
+                    + " in order to increase the available capacity from <%d> to <%d> because the buffer is already the maximum size <%d>";
+                throw new IllegalStateException(String.format(exceptionMessageFormat, buffer.length, requiredBufferSize, currentAvailableCapacity, requiredAvailableCapacity, MAX_BUFFER_SIZE));
+            } else if (requiredBufferSize > MAX_BUFFER_SIZE) {
+                final String exceptionMessageFormat = "Cannot increase the buffer size <%d>"
+                    + " in order to increase the available capacity from <%d> to <%d> because the required buffer size <%d> exceeds the maximum buffer size <%d>";
+                throw new IllegalStateException(String.format(exceptionMessageFormat, buffer.length, currentAvailableCapacity, requiredAvailableCapacity, requiredBufferSize, MAX_BUFFER_SIZE));
+            }
+            int newBufferSize = Math.min(buffer.length + Math.max(MIN_BUFFER_SIZE, requiredAvailableCapacity), MAX_BUFFER_SIZE);
+
+            buffer = Arrays.copyOf(buffer, newBufferSize);
+        }
+    }
+
+    void updateIndexes(int b, int indexOffset) {
+        if (startOfBlockIndex < 0 && b == MllpProtocolConstants.START_OF_BLOCK) {
+            startOfBlockIndex = availableByteCount + indexOffset;
+        } else if (endOfBlockIndex < 0 && b == MllpProtocolConstants.END_OF_BLOCK) {
+            endOfBlockIndex = availableByteCount + indexOffset;
+        }
+    }
+
+    void readSocketInputStream(InputStream socketInputStream, Socket socket) throws MllpSocketException, SocketTimeoutException {
+        log.trace("Entering readSocketInputStream - size = {}", size());
+        try {
+            int readCount = socketInputStream.read(buffer, availableByteCount, buffer.length - availableByteCount);
+            if (readCount == MllpProtocolConstants.END_OF_STREAM) {
+                final String exceptionMessage = "END_OF_STREAM returned from SocketInputStream.read(byte[], off, len)";
+                resetSocket(socket, exceptionMessage);
+                throw new SocketException(exceptionMessage);
+            }
+            if (readCount > 0) {
+                for (int i = 0; (startOfBlockIndex == -1 || endOfBlockIndex == -1) && i < readCount; ++i) {
+                    updateIndexes(buffer[availableByteCount + i], i);
+                }
+                availableByteCount += readCount;
+                log.trace("Read {} bytes for a total of {} bytes", readCount, availableByteCount);
+            }
+        } catch (SocketTimeoutException timeoutEx) {
+            throw timeoutEx;
+        } catch (SocketException socketEx) {
+            final String exceptionMessage = "SocketException encountered in readSocketInputStream";
+            resetSocket(socket, exceptionMessage);
+            throw new MllpSocketException(exceptionMessage, socketEx);
+        } catch (IOException ioEx) {
+            final String exceptionMessage = "IOException thrown from SocketInputStream.read(byte[], off, len)";
+            resetSocket(socket, exceptionMessage);
+            throw new MllpSocketException(exceptionMessage, ioEx);
+        } finally {
+            log.trace("Exiting readSocketInputStream - size = {}", size());
+        }
+    }
+
+    public void closeSocket(Socket socket) {
+        doSocketClose(socket, null, false);
+    }
+
+    public void closeSocket(Socket socket, String logMessage) {
+        doSocketClose(socket, logMessage, false);
+    }
+
+    public void resetSocket(Socket socket) {
+        doSocketClose(socket, null, true);
+    }
+
+    public void resetSocket(Socket socket, String logMessage) {
+        doSocketClose(socket, logMessage, true);
+    }
+
+    void doSocketClose(Socket socket, String logMessage, boolean reset) {
+        if (socket != null && socket.isConnected() && !socket.isClosed()) {
+            if (logMessage != null && !logMessage.isEmpty()) {
+                log.info("{} - {} socket {}", reset ? "Resetting" : "Closing", logMessage, socket);
+            } else {
+                log.info("{} socket {}", reset ? "Resetting" : "Closing", socket);
+            }
+
+            endpoint.updateLastConnectionTerminatedTicks();
+
+            if (!socket.isInputShutdown()) {
+                try {
+                    socket.shutdownInput();
+                } catch (IOException ignoredEx) {
+                    // TODO: Maybe log this
+                }
+            }
+
+            if (!socket.isOutputShutdown()) {
+                try {
+                    socket.shutdownOutput();
+                } catch (IOException ignoredEx) {
+                    // TODO: Maybe log this
+                }
+            }
+
+            if (reset) {
+                try {
+                    final boolean on = true;
+                    final int linger = 0;
+                    socket.setSoLinger(on, linger);
+                } catch (IOException ignoredEx) {
+                    // TODO: Maybe log this
+                }
+            }
+
+            try {
+                socket.close();
+            } catch (IOException ignoredEx) {
+                // TODO: Maybe log this
+            }
+        }
+    }
+
+    public static boolean isConnectionValid(Socket socket) {
+        return socket != null && socket.isConnected() && !socket.isClosed();
+    }
+
+    public static String formatAddressString(SocketAddress sourceAddress, SocketAddress targetAddress) {
+        String sourceAddressString = null;
+        String targetAddressString = null;
+
+        if (sourceAddress != null) {
+            sourceAddressString = sourceAddress.toString();
+        }
+
+        if (targetAddress != null) {
+            targetAddressString = targetAddress.toString();
+        }
+
+        return String.format("%s => %s", sourceAddressString, targetAddressString);
+    }
+}
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/internal/TcpServerAcceptRunnable.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/internal/TcpServerAcceptRunnable.java
new file mode 100644
index 0000000..568a56a
--- /dev/null
+++ b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/internal/TcpServerAcceptRunnable.java
@@ -0,0 +1,150 @@
+/**
+ * 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.internal;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.SocketTimeoutException;
+
+import org.apache.camel.Route;
+import org.apache.camel.component.mllp.MllpTcpServerConsumer;
+import org.apache.camel.impl.MDCUnitOfWork;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
+
+/**
+ * Runnable to handle the ServerSocket.accept requests
+ */
+public class TcpServerAcceptRunnable implements Runnable {
+    Logger log = LoggerFactory.getLogger(this.getClass());
+
+    MllpTcpServerConsumer consumer;
+    ServerSocket serverSocket;
+    boolean running;
+
+    public TcpServerAcceptRunnable(MllpTcpServerConsumer consumer, ServerSocket serverSocket) {
+        this.consumer = consumer;
+        this.serverSocket = serverSocket;
+    }
+
+    /**
+     * Derive a thread name from the class name, the component URI and the connection information.
+     * <p/>
+     * The String will in the format <class name>[endpoint key] - [local socket address]
+     *
+     * @return String for thread name
+     */
+    String createThreadName(ServerSocket serverSocket) {
+        // Get the classname without the package.  This is a nested class, so we want the parent class name included
+        String fullClassName = this.getClass().getName();
+        String className = fullClassName.substring(fullClassName.lastIndexOf('.') + 1);
+
+        // Get the URI without options
+        String fullEndpointKey = consumer.getEndpoint().getEndpointKey();
+        String endpointKey;
+        if (fullEndpointKey.contains("?")) {
+            endpointKey = fullEndpointKey.substring(0, fullEndpointKey.indexOf('?'));
+        } else {
+            endpointKey = fullEndpointKey;
+        }
+
+        // Now put it all together
+        return String.format("%s[%s] - %s", className, endpointKey, serverSocket.getLocalSocketAddress());
+    }
+
+    /**
+     * The main ServerSocket.accept() loop
+     * <p/>
+     * NOTE:  When a connection is received, the Socket is checked after a brief delay in an attempt to determine if this is a load-balancer probe.  The test is done before the
+     * ConsumerClientSocketThread is created to avoid creating a large number of short lived threads, which is what can occur if the load balancer polling interval is very short.
+     */
+    public void run() {
+        running = true;
+        String originalThreadName = Thread.currentThread().getName();
+        Thread.currentThread().setName(createThreadName(serverSocket));
+
+        MDC.put(MDCUnitOfWork.MDC_CAMEL_CONTEXT_ID, consumer.getEndpoint().getCamelContext().getName());
+
+        Route route = consumer.getRoute();
+        if (route != null) {
+            String routeId = route.getId();
+            if (routeId != null) {
+                MDC.put(MDCUnitOfWork.MDC_ROUTE_ID, route.getId());
+            }
+        }
+
+        log.info("Starting ServerSocket accept thread for {}", serverSocket);
+        try {
+            while (running && null != serverSocket && serverSocket.isBound() && !serverSocket.isClosed()) {
+                Socket socket = null;
+                try {
+                    socket = serverSocket.accept();
+                    consumer.getEndpoint().updateLastConnectionEstablishedTicks();
+                } catch (SocketTimeoutException timeoutEx) {
+                    // Didn't get a new connection - keep waiting for one
+                    log.debug("Timeout waiting for client connection - keep listening");
+                    continue;
+                } catch (SocketException socketEx) {
+                    // This should happen if the component is closed while the accept call is blocking
+                    if (serverSocket.isBound()) {
+                        try {
+                            serverSocket.close();
+                        } catch (Exception ex) {
+                            log.debug("Exception encountered closing ServerSocket after SocketException on accept() - ignoring", ex);
+                        }
+                    }
+                    continue;
+                } catch (IOException ioEx) {
+                    log.error("Exception encountered accepting connection - closing ServerSocket", ioEx);
+                    if (serverSocket.isBound()) {
+                        try {
+                            serverSocket.close();
+                        } catch (Exception ex) {
+                            log.debug("Exception encountered closing ServerSocket after exception on accept() - ignoring", ex);
+                        }
+                    }
+                    continue;
+                }
+
+                if (MllpSocketBuffer.isConnectionValid(socket)) {
+                    // Try and avoid starting client threads for things like security scans and load balancer probes
+                    consumer.startConsumer(socket);
+                }
+            }
+        } finally {
+            log.info("ServerSocket.accept loop finished - closing listener");
+            if (null != serverSocket && serverSocket.isBound() && !serverSocket.isClosed()) {
+                try {
+                    serverSocket.close();
+                } catch (Exception ex) {
+                    log.debug("Exception encountered closing ServerSocket after accept loop had exited - ignoring", ex);
+                }
+            }
+            Thread.currentThread().setName(originalThreadName);
+            MDC.remove(MDCUnitOfWork.MDC_ROUTE_ID);
+            MDC.remove(MDCUnitOfWork.MDC_CAMEL_CONTEXT_ID);
+        }
+    }
+
+    public void stop() {
+        running = false;
+    }
+}
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/internal/TcpSocketConsumerRunnable.java b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/internal/TcpSocketConsumerRunnable.java
new file mode 100644
index 0000000..0f0aa56
--- /dev/null
+++ b/components/camel-mllp/src/main/java/org/apache/camel/component/mllp/internal/TcpSocketConsumerRunnable.java
@@ -0,0 +1,595 @@
+/**
+ * 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.internal;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketTimeoutException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.ExchangePattern;
+import org.apache.camel.Message;
+import org.apache.camel.Route;
+import org.apache.camel.component.mllp.MllpAcknowledgementDeliveryException;
+import org.apache.camel.component.mllp.MllpConstants;
+import org.apache.camel.component.mllp.MllpInvalidAcknowledgementException;
+import org.apache.camel.component.mllp.MllpInvalidMessageException;
+import org.apache.camel.component.mllp.MllpProtocolConstants;
+import org.apache.camel.component.mllp.MllpReceiveException;
+import org.apache.camel.component.mllp.MllpSocketException;
+import org.apache.camel.component.mllp.MllpTcpServerConsumer;
+import org.apache.camel.converter.IOConverter;
+import org.apache.camel.impl.MDCUnitOfWork;
+import org.apache.camel.processor.mllp.Hl7AcknowledgementGenerationException;
+import org.apache.camel.util.IOHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
+
+/**
+ * Runnable to read the Socket
+ */
+public class TcpSocketConsumerRunnable implements Runnable {
+    final Socket clientSocket;
+    final MllpSocketBuffer mllpBuffer;
+
+    Logger log = LoggerFactory.getLogger(this.getClass());
+    MllpTcpServerConsumer consumer;
+    boolean running;
+
+    private final String localAddress;
+    private final String remoteAddress;
+    private final String combinedAddress;
+
+    public TcpSocketConsumerRunnable(MllpTcpServerConsumer consumer, Socket clientSocket) {
+        this.consumer = consumer;
+        // this.setName(createThreadName(clientSocket));
+        this.clientSocket = clientSocket;
+
+        SocketAddress localSocketAddress = clientSocket.getLocalSocketAddress();
+        if (localSocketAddress != null) {
+            localAddress = localSocketAddress.toString();
+        } else {
+            localAddress = null;
+        }
+
+        SocketAddress remoteSocketAddress = clientSocket.getRemoteSocketAddress();
+        if (remoteSocketAddress != null) {
+            remoteAddress = remoteSocketAddress.toString();
+        } else {
+            remoteAddress = null;
+        }
+
+        combinedAddress = MllpSocketBuffer.formatAddressString(remoteSocketAddress, localSocketAddress);
+
+
+        try {
+            if (consumer.getConfiguration().hasKeepAlive()) {
+                this.clientSocket.setKeepAlive(consumer.getConfiguration().getKeepAlive());
+            }
+            if (consumer.getConfiguration().hasTcpNoDelay()) {
+                this.clientSocket.setTcpNoDelay(consumer.getConfiguration().getTcpNoDelay());
+            }
+            if (consumer.getConfiguration().hasReceiveBufferSize()) {
+                this.clientSocket.setReceiveBufferSize(consumer.getConfiguration().getReceiveBufferSize());
+            }
+            if (consumer.getConfiguration().hasSendBufferSize()) {
+                this.clientSocket.setSendBufferSize(consumer.getConfiguration().getSendBufferSize());
+            }
+
+            this.clientSocket.setSoLinger(false, -1);
+
+            // Initial Read Timeout
+            this.clientSocket.setSoTimeout(consumer.getConfiguration().getReceiveTimeout());
+        } catch (IOException initializationException) {
+            throw new IllegalStateException("Failed to initialize " + this.getClass().getSimpleName(), initializationException);
+        }
+
+        mllpBuffer = new MllpSocketBuffer(consumer.getEndpoint());
+    }
+
+    /**
+     * derive a thread name from the class name, the component URI and the connection information
+     * <p/>
+     * The String will in the format <class name>[endpoint key] - [local socket address] -> [remote socket address]
+     *
+     * @return the thread name
+     */
+    String createThreadName(Socket socket) {
+        // Get the URI without options
+        String fullEndpointKey = consumer.getEndpoint().getEndpointKey();
+        String endpointKey;
+        if (fullEndpointKey.contains("?")) {
+            endpointKey = fullEndpointKey.substring(0, fullEndpointKey.indexOf('?'));
+        } else {
+            endpointKey = fullEndpointKey;
+        }
+
+        // Now put it all together
+        return String.format("%s[%s] - %s", this.getClass().getSimpleName(), endpointKey, combinedAddress);
+    }
+
+    private void sendAcknowledgement(byte[] originalHl7MessageBytes, Exchange exchange) {
+        log.trace("entering sendAcknowledgement(byte[], Exchange)");
+
+        consumer.getEndpoint().checkBeforeSendProperties(exchange, clientSocket, log);
+
+        // Find the acknowledgement body
+        byte[] acknowledgementMessageBytes = exchange.getProperty(MllpConstants.MLLP_ACKNOWLEDGEMENT, byte[].class);
+        if (acknowledgementMessageBytes == null) {
+            acknowledgementMessageBytes = exchange.getProperty(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING, byte[].class);
+        }
+
+        String acknowledgementMessageType = null;
+        if (null == acknowledgementMessageBytes) {
+
+            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);
+                if (acknowledgementBytesProperty == null && acknowledgementStringProperty == null) {
+                    final String exceptionMessage = "Automatic Acknowledgement is disabled and the "
+                        + MllpConstants.MLLP_ACKNOWLEDGEMENT + " and " + MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING + " exchange properties are null";
+                    exchange.setException(new MllpInvalidAcknowledgementException(exceptionMessage, originalHl7MessageBytes, acknowledgementMessageBytes));
+                } else {
+                    final String exceptionMessage = "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[]";
+                    exchange.setException(new MllpInvalidAcknowledgementException(exceptionMessage, originalHl7MessageBytes, acknowledgementMessageBytes));
+                }
+            } else {
+                String acknowledgmentTypeProperty = exchange.getProperty(MllpConstants.MLLP_ACKNOWLEDGEMENT_TYPE, String.class);
+                try {
+                    if (null == acknowledgmentTypeProperty) {
+                        if (null == exchange.getException()) {
+                            acknowledgementMessageType = "AA";
+                        } else {
+                            acknowledgementMessageType = "AE";
+                        }
+                    } else {
+                        switch (acknowledgmentTypeProperty) {
+                        case "AA":
+                            acknowledgementMessageType = "AA";
+                            break;
+                        case "AE":
+                            acknowledgementMessageType = "AE";
+                            break;
+                        case "AR":
+                            acknowledgementMessageType = "AR";
+                            break;
+                        default:
+                            exchange.setException(new Hl7AcknowledgementGenerationException("Unsupported acknowledgment type: " + acknowledgmentTypeProperty));
+                            return;
+                        }
+                    }
+
+                    Hl7Util.generateAcknowledgementPayload(mllpBuffer, originalHl7MessageBytes, acknowledgementMessageType);
+
+                } catch (Hl7AcknowledgementGenerationException ackGenerationException) {
+                    exchange.setProperty(MllpConstants.MLLP_ACKNOWLEDGEMENT_EXCEPTION, ackGenerationException);
+                    exchange.setException(ackGenerationException);
+                }
+            }
+        } else {
+            mllpBuffer.setEnvelopedMessage(acknowledgementMessageBytes);
+
+            final byte bM = 77;
+            final byte bS = 83;
+            final byte bA = 65;
+            final byte bE = 69;
+            final byte bR = 82;
+
+            final byte fieldSeparator = originalHl7MessageBytes[3];
+            // Acknowledgment is specified in exchange property - determine the acknowledgement type
+            for (int i = 0; i < originalHl7MessageBytes.length; ++i) {
+                if (MllpProtocolConstants.SEGMENT_DELIMITER == i) {
+                    if (i + 7 < originalHl7MessageBytes.length // Make sure we don't run off the end of the message
+                        && bM == originalHl7MessageBytes[i + 1] && bS == originalHl7MessageBytes[i + 2]
+                        && bA == originalHl7MessageBytes[i + 3] && fieldSeparator == originalHl7MessageBytes[i + 4]) {
+                        if (fieldSeparator != originalHl7MessageBytes[i + 7]) {
+                            log.warn("MSA-1 is longer than 2-bytes - ignoring trailing bytes");
+                        }
+                        // Found MSA - pull acknowledgement bytes
+                        byte[] acknowledgmentTypeBytes = new byte[2];
+                        acknowledgmentTypeBytes[0] = originalHl7MessageBytes[i + 5];
+                        acknowledgmentTypeBytes[1] = originalHl7MessageBytes[i + 6];
+                        try {
+                            acknowledgementMessageType = IOConverter.toString(acknowledgmentTypeBytes, exchange);
+                        } catch (IOException ioEx) {
+                            throw new RuntimeException("Failed to convert acknowledgement message to string", ioEx);
+                        }
+
+                        // Verify it's a valid acknowledgement code
+                        if (bA != acknowledgmentTypeBytes[0]) {
+                            switch (acknowledgementMessageBytes[1]) {
+                            case bA:
+                            case bR:
+                            case bE:
+                                break;
+                            default:
+                                log.warn("Invalid acknowledgement type [" + acknowledgementMessageType + "] found in message - should be AA, AE or AR");
+                            }
+                        }
+
+                        // if the MLLP_ACKNOWLEDGEMENT_TYPE property is set on the exchange, make sure it matches
+                        String acknowledgementTypeProperty = exchange.getProperty(MllpConstants.MLLP_ACKNOWLEDGEMENT_TYPE, String.class);
+                        if (null != acknowledgementTypeProperty && !acknowledgementTypeProperty.equals(acknowledgementMessageType)) {
+                            log.warn("Acknowledgement type found in message [" + acknowledgementMessageType + "] does not match "
+                                + MllpConstants.MLLP_ACKNOWLEDGEMENT_TYPE + " exchange property value [" + acknowledgementTypeProperty + "] - using value found in message");
+                        }
+                    }
+                }
+            }
+        }
+
+        Message message = exchange.hasOut() ? exchange.getOut() : exchange.getIn();
+        if (acknowledgementMessageType != null && !acknowledgementMessageType.isEmpty()) {
+            message.setHeader(MllpConstants.MLLP_ACKNOWLEDGEMENT_TYPE, acknowledgementMessageType);
+        }
+
+        if (mllpBuffer.hasCompleteEnvelope()) {
+            // The mllpBuffer will be used if bufferWrites is set or if auto acknowledgement is used
+            message.setHeader(MllpConstants.MLLP_ACKNOWLEDGEMENT, mllpBuffer.toMllpPayload());
+            message.setHeader(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING, mllpBuffer.toHl7String(IOHelper.getCharsetName(exchange, false)));
+
+            // Send the acknowledgement
+            if (log.isDebugEnabled()) {
+                log.debug("Sending Acknowledgement: {}", mllpBuffer.toPrintFriendlyHl7String());
+            }
+
+            try {
+                mllpBuffer.writeTo(clientSocket);
+            } catch (MllpSocketException acknowledgementDeliveryEx) {
+                Exception exchangeEx = new MllpAcknowledgementDeliveryException("Failure delivering acknowledgment", originalHl7MessageBytes, acknowledgementMessageBytes, acknowledgementDeliveryEx);
+                exchange.setProperty(MllpConstants.MLLP_ACKNOWLEDGEMENT_EXCEPTION, acknowledgementDeliveryEx);
+                exchange.setException(exchangeEx);
+            } finally {
+                mllpBuffer.reset();
+            }
+        } else if (acknowledgementMessageBytes != null && acknowledgementMessageBytes.length > 0) {
+            message.setHeader(MllpConstants.MLLP_ACKNOWLEDGEMENT, acknowledgementMessageBytes);
+            String acknowledgementMessageString = "";
+            String exchangeCharset = IOHelper.getCharsetName(exchange, false);
+            if (exchangeCharset != null && !exchangeCharset.isEmpty()) {
+                try {
+                    acknowledgementMessageString = new String(acknowledgementMessageBytes, exchangeCharset);
+                } catch (UnsupportedEncodingException e) {
+                    log.warn("Failed to covert acknowledgment to string using {} charset - falling back to default charset {}", exchange, MllpProtocolConstants.DEFAULT_CHARSET);
+                    acknowledgementMessageString = new String(acknowledgementMessageBytes, MllpProtocolConstants.DEFAULT_CHARSET);
+                }
+            } else {
+                acknowledgementMessageString = new String(acknowledgementMessageBytes, MllpProtocolConstants.DEFAULT_CHARSET);
+            }
+            message.setHeader(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING, acknowledgementMessageString);
+
+            // Send the acknowledgement
+            if (log.isDebugEnabled()) {
+                log.debug("Sending Acknowledgement: {}", Hl7Util.convertToPrintFriendlyString(acknowledgementMessageBytes));
+            }
+
+            try {
+                mllpBuffer.setEnvelopedMessage(acknowledgementMessageBytes);
+                mllpBuffer.writeTo(clientSocket);
+            } catch (MllpSocketException acknowledgementDeliveryEx) {
+                Exception exchangeEx = new MllpAcknowledgementDeliveryException("Failure delivering acknowledgment", originalHl7MessageBytes, acknowledgementMessageBytes, acknowledgementDeliveryEx);
+                exchange.setProperty(MllpConstants.MLLP_ACKNOWLEDGEMENT_EXCEPTION, acknowledgementDeliveryEx);
+                exchange.setException(exchangeEx);
+            }
+        }
+
+        consumer.getEndpoint().checkAfterSendProperties(exchange, clientSocket, log);
+    }
+
+    private void populateHl7DataHeaders(Exchange exchange, Message message, byte[] hl7MessageBytes) {
+        if (exchange != null && exchange.getException() == null) {
+            if (hl7MessageBytes == null || hl7MessageBytes.length < 8) {
+                // Not enough data to populate anything - just return
+                return;
+            }
+            // Find the end of the MSH and indexes of the fields in the MSH to populate message headers
+            final byte fieldSeparator = hl7MessageBytes[3];
+            int endOfMSH = -1;
+            List<Integer> fieldSeparatorIndexes = new ArrayList<>(10);  // We should have at least 10 fields
+
+            for (int i = 0; i < hl7MessageBytes.length; ++i) {
+                if (fieldSeparator == hl7MessageBytes[i]) {
+                    fieldSeparatorIndexes.add(i);
+                } else if (MllpProtocolConstants.SEGMENT_DELIMITER == hl7MessageBytes[i]) {
+                    // If the MSH Segment doesn't have a trailing field separator, add one so the field can be extracted into a header
+                    if (fieldSeparator != hl7MessageBytes[i - 1]) {
+                        fieldSeparatorIndexes.add(i);
+                    }
+                    endOfMSH = i;
+                    break;
+                }
+            }
+
+            if (-1 == endOfMSH) {
+                // TODO:  May want to throw some sort of an Exception here
+                log.error("Population of message headers failed - unable to find the end of the MSH segment");
+            } else if (consumer.getConfiguration().isHl7Headers()) {
+                log.debug("Populating the HL7 message headers");
+                Charset charset = Charset.forName(IOHelper.getCharsetName(exchange));
+
+                for (int i = 2; i < fieldSeparatorIndexes.size(); ++i) {
+                    int startingFieldSeparatorIndex = fieldSeparatorIndexes.get(i - 1);
+                    int endingFieldSeparatorIndex = fieldSeparatorIndexes.get(i);
+
+                    // Only populate the header if there's data in the HL7 field
+                    if (endingFieldSeparatorIndex - startingFieldSeparatorIndex > 1) {
+                        String headerName = null;
+                        switch (i) {
+                        case 2: // MSH-3
+                            headerName = MllpConstants.MLLP_SENDING_APPLICATION;
+                            break;
+                        case 3: // MSH-4
+                            headerName = MllpConstants.MLLP_SENDING_FACILITY;
+                            break;
+                        case 4: // MSH-5
+                            headerName = MllpConstants.MLLP_RECEIVING_APPLICATION;
+                            break;
+                        case 5: // MSH-6
+                            headerName = MllpConstants.MLLP_RECEIVING_FACILITY;
+                            break;
+                        case 6: // MSH-7
+                            headerName = MllpConstants.MLLP_TIMESTAMP;
+                            break;
+                        case 7: // MSH-8
+                            headerName = MllpConstants.MLLP_SECURITY;
+                            break;
+                        case 8: // MSH-9
+                            headerName = MllpConstants.MLLP_MESSAGE_TYPE;
+                            break;
+                        case 9: // MSH-10
+                            headerName = MllpConstants.MLLP_MESSAGE_CONTROL;
+                            break;
+                        case 10: // MSH-11
+                            headerName = MllpConstants.MLLP_PROCESSING_ID;
+                            break;
+                        case 11: // MSH-12
+                            headerName = MllpConstants.MLLP_VERSION_ID;
+                            break;
+                        case 17: // MSH-18
+                            headerName = MllpConstants.MLLP_CHARSET;
+                            break;
+                        default:
+                            // Not processing this field
+                            continue;
+                        }
+
+                        String headerValue = new String(hl7MessageBytes, startingFieldSeparatorIndex + 1,
+                            endingFieldSeparatorIndex - startingFieldSeparatorIndex - 1,
+                            charset);
+                        message.setHeader(headerName, headerValue);
+
+                        // For MSH-9, set a couple more headers
+                        if (i == 8) {
+                            // final byte componentSeparator = hl7MessageBytes[4];
+                            String componentSeparator = new String(hl7MessageBytes, 4, 1, charset);
+                            String[] components = headerValue.split(String.format("\\Q%s\\E", componentSeparator), 3);
+                            message.setHeader(MllpConstants.MLLP_EVENT_TYPE, components[0]);
+                            if (2 <= components.length) {
+                                message.setHeader(MllpConstants.MLLP_TRIGGER_EVENT, components[1]);
+                            }
+                        }
+                    }
+                }
+            } else {
+                log.trace("HL7 Message headers disabled");
+            }
+        }
+    }
+
+
+    void processMessage(byte[] hl7MessageBytes) {
+        consumer.getEndpoint().updateLastConnectionActivityTicks();
+
+        // Send the message on to Camel for processing and wait for the response
+        log.debug("Populating the exchange with received message");
+        Exchange exchange = consumer.getEndpoint().createExchange(ExchangePattern.InOut);
+        // TODO: Evaluate the CHARSET handling - may not be correct
+        exchange.setProperty(Exchange.CHARSET_NAME, consumer.getEndpoint().determineCharset(hl7MessageBytes, null));
+        try {
+            consumer.createUoW(exchange);
+            Message message = exchange.getIn();
+
+            if (localAddress != null) {
+                message.setHeader(MllpConstants.MLLP_LOCAL_ADDRESS, localAddress);
+            }
+
+            if (remoteAddress != null) {
+                message.setHeader(MllpConstants.MLLP_REMOTE_ADDRESS, remoteAddress);
+            }
+            message.setHeader(MllpConstants.MLLP_AUTO_ACKNOWLEDGE, consumer.getConfiguration().isAutoAck());
+
+            if (consumer.getConfiguration().isValidatePayload()) {
+                String exceptionMessage = Hl7Util.generateInvalidPayloadExceptionMessage(hl7MessageBytes);
+                if (exceptionMessage != null) {
+                    exchange.setException(new MllpInvalidMessageException(exceptionMessage, hl7MessageBytes));
+                }
+            }
+            populateHl7DataHeaders(exchange, message, hl7MessageBytes);
+
+            if (consumer.getConfiguration().isStringPayload()) {
+                if (hl7MessageBytes != null && hl7MessageBytes.length > 0) {
+                    message.setBody(consumer.getEndpoint().createNewString(hl7MessageBytes, message.getHeader(MllpConstants.MLLP_CHARSET, String.class)), String.class);
+                } else {
+                    message.setBody("", String.class);
+                }
+            } else {
+                message.setBody(hl7MessageBytes, byte[].class);
+            }
+
+            log.debug("Calling processor");
+            try {
+                consumer.getProcessor().process(exchange);
+                sendAcknowledgement(hl7MessageBytes, exchange);
+            } catch (RuntimeException runtimeEx) {
+                throw runtimeEx;
+            } catch (Exception ex) {
+                log.error("Unexpected exception processing exchange", ex);
+            }
+        } catch (Exception uowEx) {
+            // TODO:  Handle this correctly
+            exchange.setException(uowEx);
+            log.warn("Exception encountered creating Unit of Work - sending exception to route", uowEx);
+            try {
+                consumer.getProcessor().process(exchange);
+            } catch (Exception e) {
+                log.error("Exception encountered processing exchange with exception encountered createing Unit of Work", e);
+            }
+            return;
+        } finally {
+            if (exchange != null) {
+                consumer.doneUoW(exchange);
+            }
+        }
+    }
+
+    @Override
+    public void run() {
+        running = true;
+        String originalThreadName = Thread.currentThread().getName();
+        Thread.currentThread().setName(createThreadName(clientSocket));
+        MDC.put(MDCUnitOfWork.MDC_CAMEL_CONTEXT_ID, consumer.getEndpoint().getCamelContext().getName());
+
+        Route route = consumer.getRoute();
+        if (route != null) {
+            String routeId = route.getId();
+            if (routeId != null) {
+                MDC.put(MDCUnitOfWork.MDC_ROUTE_ID, route.getId());
+            }
+        }
+
+        log.debug("Starting {} for {}", this.getClass().getSimpleName(), combinedAddress);
+        try {
+            while (running && null != clientSocket && clientSocket.isConnected() && !clientSocket.isClosed()) {
+                byte[] hl7MessageBytes = null;
+                log.debug("Checking for data ....");
+                try {
+                    mllpBuffer.readFrom(clientSocket);
+                    if (mllpBuffer.hasCompleteEnvelope()) {
+                        hl7MessageBytes = mllpBuffer.toMllpPayload();
+                        log.debug("Received {} byte message {}", hl7MessageBytes.length, Hl7Util.convertToPrintFriendlyString(hl7MessageBytes));
+                        if (mllpBuffer.hasLeadingOutOfBandData()) {
+                            // TODO:  Move the convertion utilities to the MllpSocketBuffer to avoid a byte[] copy
+                            log.warn("Ignoring leading out-of-band data: {}", Hl7Util.convertToPrintFriendlyString(mllpBuffer.getLeadingOutOfBandData()));
+                        }
+                        if (mllpBuffer.hasTrailingOutOfBandData()) {
+                            log.warn("Ignoring trailing out-of-band data: {}", Hl7Util.convertToPrintFriendlyString(mllpBuffer.getTrailingOutOfBandData()));
+                        }
+                        mllpBuffer.reset();
+
+                        processMessage(hl7MessageBytes);
+                    } else if (!mllpBuffer.hasStartOfBlock()) {
+                        byte[] payload = mllpBuffer.toByteArray();
+                        log.warn("Ignoring {} byte un-enveloped payload {}", payload.length, Hl7Util.convertToPrintFriendlyString(payload));
+                        mllpBuffer.reset();
+                    } else if (!mllpBuffer.isEmpty()) {
+                        byte[] payload = mllpBuffer.toByteArray();
+                        log.warn("Partial {} byte payload received {}", payload.length, Hl7Util.convertToPrintFriendlyString(payload));
+                    }
+                } catch (SocketTimeoutException timeoutEx) {
+                    if (mllpBuffer.isEmpty()) {
+                        if (consumer.getConfiguration().hasIdleTimeout()) {
+                            long currentTicks = System.currentTimeMillis();
+                            long lastReceivedMessageTicks = consumer.getConsumerRunnables().get(this);
+                            long idleTime = currentTicks - lastReceivedMessageTicks;
+                            if (idleTime >= consumer.getConfiguration().getIdleTimeout()) {
+                                consumer.getEndpoint().doConnectionClose(clientSocket, true, log);
+                            }
+                        }
+                        log.info("No data received - ignoring timeout");
+                    } else {
+                        mllpBuffer.resetSocket(clientSocket);
+                        if (consumer.getEndpoint().isBridgeErrorHandler()) {
+                            Exchange exchange = consumer.getEndpoint().createExchange(ExchangePattern.InOut);
+                            exchange.setException(new MllpInvalidMessageException("Timeout receiving complete payload", mllpBuffer.toByteArray()));
+                            log.warn("Exception encountered reading payload - sending exception to route", exchange.getException());
+                            try {
+                                consumer.getProcessor().process(exchange);
+                            } catch (Exception e) {
+                                log.error("Exception encountered processing exchange with exception encounter reading payload", e);
+                            }
+                        } else {
+                            log.error("Timeout receiving complete payload", new MllpInvalidMessageException("Timeout receiving complete payload", mllpBuffer.toByteArray(), timeoutEx));
+                        }
+                    }
+                } catch (MllpSocketException mllpSocketEx) {
+                    if (!mllpBuffer.isEmpty()) {
+                        Exchange exchange = consumer.getEndpoint().createExchange(ExchangePattern.InOut);
+                        exchange.setException(new MllpReceiveException("Exception encountered reading payload", mllpBuffer.toByteArrayAndReset(), mllpSocketEx));
+                        try {
+                            consumer.getProcessor().process(exchange);
+                        } catch (Exception ignoredEx) {
+                            log.error("Ingnoring exception encountered processing exchange with exception encounter reading payload", ignoredEx);
+                        }
+                    } else {
+                        log.warn("Ignoring exception encountered checking for data", mllpSocketEx);
+                    }
+                }
+            }
+        } catch (Exception unexpectedEx) {
+            log.error("Unexpected exception encountered receiving messages", unexpectedEx);
+        } finally {
+            consumer.getConsumerRunnables().remove(this);
+            log.debug("{} for {} completed", this.getClass().getSimpleName(), combinedAddress);
+
+            Thread.currentThread().setName(originalThreadName);
+            MDC.remove(MDCUnitOfWork.MDC_ROUTE_ID);
+            MDC.remove(MDCUnitOfWork.MDC_CAMEL_CONTEXT_ID);
+        }
+    }
+
+    public void closeSocket() {
+        mllpBuffer.closeSocket(clientSocket);
+    }
+
+    public void closeSocket(String logMessage) {
+        mllpBuffer.closeSocket(clientSocket, logMessage);
+    }
+
+    public void resetSocket() {
+        mllpBuffer.resetSocket(clientSocket);
+    }
+
+    public void resetSocket(String logMessage) {
+        mllpBuffer.resetSocket(clientSocket, logMessage);
+    }
+
+    public void stop() {
+        running = false;
+    }
+
+    public String getLocalAddress() {
+        return localAddress;
+    }
+
+    public String getRemoteAddress() {
+        return remoteAddress;
+    }
+
+    public String getCombinedAddress() {
+        return combinedAddress;
+    }
+}
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/processor/mllp/Hl7AcknowledgementGenerationException.java b/components/camel-mllp/src/main/java/org/apache/camel/processor/mllp/Hl7AcknowledgementGenerationException.java
index 2d429c5..09d7bbb 100644
--- a/components/camel-mllp/src/main/java/org/apache/camel/processor/mllp/Hl7AcknowledgementGenerationException.java
+++ b/components/camel-mllp/src/main/java/org/apache/camel/processor/mllp/Hl7AcknowledgementGenerationException.java
@@ -14,10 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.camel.processor.mllp;
 
+/*
+ * Exception thrown by the HL7AcknowledgmentGenerator in the event of a failure.
+ */
 public class Hl7AcknowledgementGenerationException extends Exception {
-
     private final byte[] hl7Message;
 
     public Hl7AcknowledgementGenerationException(String message) {
@@ -35,6 +38,7 @@ public class Hl7AcknowledgementGenerationException extends Exception {
         this.hl7Message = hl7Message;
     }
 
+
     public byte[] getHl7Message() {
         return hl7Message;
     }
diff --git a/components/camel-mllp/src/main/java/org/apache/camel/processor/mllp/Hl7AcknowledgementGenerator.java b/components/camel-mllp/src/main/java/org/apache/camel/processor/mllp/Hl7AcknowledgementGenerator.java
index ddc1927..55aeee8 100644
--- a/components/camel-mllp/src/main/java/org/apache/camel/processor/mllp/Hl7AcknowledgementGenerator.java
+++ b/components/camel-mllp/src/main/java/org/apache/camel/processor/mllp/Hl7AcknowledgementGenerator.java
@@ -14,6 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.camel.processor.mllp;
 
 import java.io.ByteArrayOutputStream;
@@ -23,27 +24,26 @@ import java.util.List;
 import org.apache.camel.Exchange;
 import org.apache.camel.Message;
 import org.apache.camel.Processor;
+import org.apache.camel.component.mllp.MllpConstants;
+import org.apache.camel.component.mllp.MllpProtocolConstants;
+import org.apache.camel.component.mllp.internal.MllpSocketBuffer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_ACKNOWLEDGEMENT;
-import static org.apache.camel.component.mllp.MllpConstants.MLLP_ACKNOWLEDGEMENT_TYPE;
-import static org.apache.camel.component.mllp.MllpEndpoint.MESSAGE_TERMINATOR;
-import static org.apache.camel.component.mllp.MllpEndpoint.SEGMENT_DELIMITER;
-
 /**
- * A Camel Processor for generating HL7 Acknowledgements
+ * A Camel Processor for generating HL7 Acknowledgements.
  */
 public class Hl7AcknowledgementGenerator implements Processor {
     private static final Logger LOG = LoggerFactory.getLogger(Hl7AcknowledgementGenerator.class);
 
-    String defaultNack = "MSH|^~\\&|||||||NACK||P|2.2" + SEGMENT_DELIMITER
-            + "MSA|AR|" + SEGMENT_DELIMITER
-            + MESSAGE_TERMINATOR;
+    String defaultNack = "MSH|^~\\&|||||||NACK||P|2.2" + MllpProtocolConstants.SEGMENT_DELIMITER
+        + "MSA|AR|" + MllpProtocolConstants.SEGMENT_DELIMITER
+        + MllpProtocolConstants.MESSAGE_TERMINATOR;
+
 
     @Override
     public void process(Exchange exchange) throws Exception {
-        Message message;
+        Message message = null;
         if (exchange.hasOut()) {
             message = exchange.getOut();
         } else {
@@ -55,13 +55,13 @@ public class Hl7AcknowledgementGenerator implements Processor {
         byte[] acknowledgementBytes = null;
         if (null == exchange.getException()) {
             acknowledgementBytes = generateApplicationAcceptAcknowledgementMessage(hl7Bytes);
-            exchange.setProperty(MLLP_ACKNOWLEDGEMENT_TYPE, "AA");
+            exchange.setProperty(MllpConstants.MLLP_ACKNOWLEDGEMENT_TYPE, "AA");
         } else {
             acknowledgementBytes = generateApplicationErrorAcknowledgementMessage(hl7Bytes);
-            exchange.setProperty(MLLP_ACKNOWLEDGEMENT_TYPE, "AE");
+            exchange.setProperty(MllpConstants.MLLP_ACKNOWLEDGEMENT_TYPE, "AE");
         }
 
-        exchange.setProperty(MLLP_ACKNOWLEDGEMENT, acknowledgementBytes);
+        exchange.setProperty(MllpConstants.MLLP_ACKNOWLEDGEMENT, acknowledgementBytes);
     }
 
     public byte[] generateApplicationAcceptAcknowledgementMessage(byte[] hl7MessageBytes) throws Hl7AcknowledgementGenerationException {
@@ -91,7 +91,7 @@ public class Hl7AcknowledgementGenerator implements Processor {
         for (int i = 0; i < hl7MessageBytes.length; ++i) {
             if (fieldSeparator == hl7MessageBytes[i]) {
                 fieldSeparatorIndexes.add(i);
-            } else if (SEGMENT_DELIMITER == hl7MessageBytes[i]) {
+            } else if (MllpProtocolConstants.SEGMENT_DELIMITER == hl7MessageBytes[i]) {
                 endOfMSH = i;
                 break;
             }
@@ -103,7 +103,7 @@ public class Hl7AcknowledgementGenerator implements Processor {
 
         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);
+                + fieldSeparatorIndexes.size() + " " + "were found", hl7MessageBytes);
         }
 
         // Build the MSH Segment
@@ -132,19 +132,21 @@ public class Hl7AcknowledgementGenerator implements Processor {
         }
 
         acknowledgement.write(hl7MessageBytes, fieldSeparatorIndexes.get(8), endOfMSH - fieldSeparatorIndexes.get(8)); // MSH-10 through the end of the MSH
-        acknowledgement.write(SEGMENT_DELIMITER);
+        acknowledgement.write(MllpProtocolConstants.SEGMENT_DELIMITER);
 
         // Build the MSA Segment
         acknowledgement.write("MSA".getBytes(), 0, 3);
         acknowledgement.write(fieldSeparator);
         acknowledgement.write(acknowledgementCode.getBytes(), 0, 2);
         acknowledgement.write(hl7MessageBytes, fieldSeparatorIndexes.get(8), fieldSeparatorIndexes.get(9) - fieldSeparatorIndexes.get(8)); // MSH-10 end
-        acknowledgement.write(SEGMENT_DELIMITER);
+        acknowledgement.write(MllpProtocolConstants.SEGMENT_DELIMITER);
 
         // Terminate the message
-        acknowledgement.write(SEGMENT_DELIMITER);
-        acknowledgement.write(MESSAGE_TERMINATOR);
+        // acknowledgement.write(SEGMENT_DELIMITER);
+        // acknowledgement.write(MESSAGE_TERMINATOR);
 
         return acknowledgement.toByteArray();
     }
+
+
 }
diff --git a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpAcknowledgementDeliveryExceptionTest.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpAcknowledgementDeliveryExceptionTest.java
new file mode 100644
index 0000000..29745ec
--- /dev/null
+++ b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpAcknowledgementDeliveryExceptionTest.java
@@ -0,0 +1,97 @@
+/**
+ * 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.junit.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for the  class.
+ */
+public class MllpAcknowledgementDeliveryExceptionTest extends MllpExceptionTestSupport {
+    static final String TEST_EXCEPTION_MESSAGE = "HL7 Acknowledgment Delivery Failed";
+
+    MllpAcknowledgementDeliveryException instance;
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorOne() throws Exception {
+        instance = new MllpAcknowledgementDeliveryException(HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES);
+
+        assertTrue(instance.getMessage().startsWith(TEST_EXCEPTION_MESSAGE));
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.hl7AcknowledgementBytes);
+    }
+
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorTwo() throws Exception {
+        instance = new MllpAcknowledgementDeliveryException(HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES, CAUSE);
+
+        assertSame(CAUSE, instance.getCause());
+        assertTrue(instance.getMessage().startsWith(TEST_EXCEPTION_MESSAGE));
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.hl7AcknowledgementBytes);
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorThree() throws Exception {
+        final String alternateExceptionMessage = "Alternate Message";
+
+        instance = new MllpAcknowledgementDeliveryException(alternateExceptionMessage, HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES);
+
+        assertTrue(instance.getMessage().startsWith(alternateExceptionMessage));
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.hl7AcknowledgementBytes);
+    }
+
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorFour() throws Exception {
+        final String alternateExceptionMessage = "Alternate Message";
+
+        instance = new MllpAcknowledgementDeliveryException(alternateExceptionMessage, HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES, CAUSE);
+
+        assertSame(CAUSE, instance.getCause());
+        assertTrue(instance.getMessage().startsWith(alternateExceptionMessage));
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.hl7AcknowledgementBytes);
+    }
+}
\ No newline at end of file
diff --git a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpAcknowledgementExceptionTest.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpAcknowledgementExceptionTest.java
new file mode 100644
index 0000000..6e6460a
--- /dev/null
+++ b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpAcknowledgementExceptionTest.java
@@ -0,0 +1,157 @@
+/**
+ * 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.junit.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for the  class.
+ */
+public class MllpAcknowledgementExceptionTest extends MllpExceptionTestSupport {
+    static final String TEST_EXCEPTION_MESSAGE = "Test Acknowledgment Message";
+
+    MllpAcknowledgementException instance;
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorOne() throws Exception {
+        instance = new MllpAcknowledgementExceptionStub(TEST_EXCEPTION_MESSAGE);
+
+        assertNull(instance.getCause());
+        assertTrue(instance.getMessage().startsWith(TEST_EXCEPTION_MESSAGE));
+        assertNull(instance.hl7MessageBytes);
+        assertNull(instance.hl7AcknowledgementBytes);
+    }
+
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorTwo() throws Exception {
+        instance = new MllpAcknowledgementExceptionStub(TEST_EXCEPTION_MESSAGE, CAUSE);
+
+        assertSame(CAUSE, instance.getCause());
+        assertTrue(instance.getMessage().startsWith(TEST_EXCEPTION_MESSAGE));
+        assertNull(instance.hl7MessageBytes);
+        assertNull(instance.hl7AcknowledgementBytes);
+    }
+
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorThree() throws Exception {
+        instance = new MllpAcknowledgementExceptionStub(TEST_EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES);
+
+        assertNull(instance.getCause());
+        assertTrue(instance.getMessage().startsWith(TEST_EXCEPTION_MESSAGE));
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertNull(instance.hl7AcknowledgementBytes);
+    }
+
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorFour() throws Exception {
+        instance = new MllpAcknowledgementExceptionStub(TEST_EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES);
+
+        assertNull(instance.getCause());
+        assertTrue(instance.getMessage().startsWith(TEST_EXCEPTION_MESSAGE));
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.hl7AcknowledgementBytes);
+    }
+
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorFive() throws Exception {
+        instance = new MllpAcknowledgementExceptionStub(TEST_EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, CAUSE);
+
+        assertSame(CAUSE, instance.getCause());
+        assertTrue(instance.getMessage().startsWith(TEST_EXCEPTION_MESSAGE));
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertNull(instance.hl7AcknowledgementBytes);
+    }
+
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorSix() throws Exception {
+        instance = new MllpAcknowledgementExceptionStub(TEST_EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES, CAUSE);
+
+        assertSame(CAUSE, instance.getCause());
+        assertTrue(instance.getMessage().startsWith(TEST_EXCEPTION_MESSAGE));
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.hl7AcknowledgementBytes);
+    }
+
+
+    static class MllpAcknowledgementExceptionStub extends MllpAcknowledgementException {
+        MllpAcknowledgementExceptionStub(String message) {
+            super(message);
+        }
+
+        MllpAcknowledgementExceptionStub(String message, Throwable cause) {
+            super(message, cause);
+        }
+
+        MllpAcknowledgementExceptionStub(String message, byte[] hl7Message) {
+            super(message, hl7Message);
+        }
+
+        MllpAcknowledgementExceptionStub(String message, byte[] hl7Message, byte[] hl7Acknowledgement) {
+            super(message, hl7Message, hl7Acknowledgement);
+        }
+
+        MllpAcknowledgementExceptionStub(String message, byte[] hl7Message, Throwable cause) {
+            super(message, hl7Message, cause);
+        }
+
+        MllpAcknowledgementExceptionStub(String message, byte[] hl7Message, byte[] hl7Acknowledgement, Throwable cause) {
+            super(message, hl7Message, hl7Acknowledgement, cause);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpAcknowledgementReceiveExceptionTest.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpAcknowledgementReceiveExceptionTest.java
new file mode 100644
index 0000000..1966ede
--- /dev/null
+++ b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpAcknowledgementReceiveExceptionTest.java
@@ -0,0 +1,154 @@
+/**
+ * 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.junit.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for the  class.
+ */
+public class MllpAcknowledgementReceiveExceptionTest extends MllpExceptionTestSupport {
+    static final String ALTERNATE_EXCEPTION_MESSAGE = "Alternate Acknowledgment Exception Message";
+
+    MllpAcknowledgementReceiveException instance;
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorOne() throws Exception {
+        instance = new MllpAcknowledgementReceiveException(HL7_MESSAGE_BYTES);
+
+        assertTrue(instance.getMessage().startsWith(MllpAcknowledgementReceiveException.EXCEPTION_MESSAGE));
+        assertNull(instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertNull(instance.hl7AcknowledgementBytes);
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorTwo() throws Exception {
+        instance = new MllpAcknowledgementReceiveException(HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES);
+
+        assertTrue(instance.getMessage().startsWith(MllpAcknowledgementReceiveException.EXCEPTION_MESSAGE));
+        assertNull(instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.hl7AcknowledgementBytes);
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorThree() throws Exception {
+        instance = new MllpAcknowledgementReceiveException(HL7_MESSAGE_BYTES, CAUSE);
+
+        assertTrue(instance.getMessage().startsWith(MllpAcknowledgementReceiveException.EXCEPTION_MESSAGE));
+        assertSame(CAUSE, instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertNull(instance.hl7AcknowledgementBytes);
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorFour() throws Exception {
+        instance = new MllpAcknowledgementReceiveException(HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES, CAUSE);
+
+        assertTrue(instance.getMessage().startsWith(MllpAcknowledgementReceiveException.EXCEPTION_MESSAGE));
+        assertSame(CAUSE, instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.hl7AcknowledgementBytes);
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorFive() throws Exception {
+        instance = new MllpAcknowledgementReceiveException(ALTERNATE_EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES);
+
+        assertTrue(instance.getMessage().startsWith(ALTERNATE_EXCEPTION_MESSAGE));
+        assertNull(instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertNull(instance.hl7AcknowledgementBytes);
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorSix() throws Exception {
+        instance = new MllpAcknowledgementReceiveException(ALTERNATE_EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES);
+
+        assertTrue(instance.getMessage().startsWith(ALTERNATE_EXCEPTION_MESSAGE));
+        assertNull(instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.hl7AcknowledgementBytes);
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorSeven() throws Exception {
+        instance = new MllpAcknowledgementReceiveException(ALTERNATE_EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, CAUSE);
+
+        assertTrue(instance.getMessage().startsWith(ALTERNATE_EXCEPTION_MESSAGE));
+        assertSame(CAUSE, instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertNull(instance.hl7AcknowledgementBytes);
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorEight() throws Exception {
+        instance = new MllpAcknowledgementReceiveException(ALTERNATE_EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES, CAUSE);
+
+        assertTrue(instance.getMessage().startsWith(ALTERNATE_EXCEPTION_MESSAGE));
+        assertSame(CAUSE, instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.hl7AcknowledgementBytes);
+    }
+}
\ No newline at end of file
diff --git a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpAcknowledgementTimeoutExceptionTest.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpAcknowledgementTimeoutExceptionTest.java
new file mode 100644
index 0000000..9fb85cd
--- /dev/null
+++ b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpAcknowledgementTimeoutExceptionTest.java
@@ -0,0 +1,173 @@
+/**
+ * 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.junit.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for the  class.
+ */
+public class MllpAcknowledgementTimeoutExceptionTest extends MllpExceptionTestSupport {
+    static final String ALTERNATE_EXCEPTION_MESSAGE = "Test Message";
+
+    MllpAcknowledgementTimeoutException instance;
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorOne() throws Exception {
+        instance = new MllpAcknowledgementTimeoutException(HL7_MESSAGE_BYTES);
+
+        assertTrue(instance.getMessage().startsWith(MllpAcknowledgementTimeoutException.EXCEPTION_MESSAGE));
+        assertNull(instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertNull(instance.hl7AcknowledgementBytes);
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorTwo() throws Exception {
+        instance = new MllpAcknowledgementTimeoutException(HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES);
+
+        assertTrue(instance.getMessage().startsWith(MllpAcknowledgementTimeoutException.EXCEPTION_MESSAGE));
+        assertNull(instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.hl7AcknowledgementBytes);
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorThree() throws Exception {
+        instance = new MllpAcknowledgementTimeoutException(HL7_MESSAGE_BYTES, CAUSE);
+
+        assertTrue(instance.getMessage().startsWith(MllpAcknowledgementTimeoutException.EXCEPTION_MESSAGE));
+        assertSame(CAUSE, instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertNull(instance.hl7AcknowledgementBytes);
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorFour() throws Exception {
+        instance = new MllpAcknowledgementTimeoutException(HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES, CAUSE);
+
+        assertTrue(instance.getMessage().startsWith(MllpAcknowledgementTimeoutException.EXCEPTION_MESSAGE));
+        assertSame(CAUSE, instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.hl7AcknowledgementBytes);
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorFive() throws Exception {
+        instance = new MllpAcknowledgementTimeoutException(ALTERNATE_EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES);
+
+        assertTrue(instance.getMessage().startsWith(ALTERNATE_EXCEPTION_MESSAGE));
+        assertNull(instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertNull(instance.hl7AcknowledgementBytes);
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorSix() throws Exception {
+        instance = new MllpAcknowledgementTimeoutException(ALTERNATE_EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES);
+
+        assertTrue(instance.getMessage().startsWith(ALTERNATE_EXCEPTION_MESSAGE));
+        assertNull(instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.hl7AcknowledgementBytes);
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorSeven() throws Exception {
+        instance = new MllpAcknowledgementTimeoutException(ALTERNATE_EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, CAUSE);
+
+        assertTrue(instance.getMessage().startsWith(ALTERNATE_EXCEPTION_MESSAGE));
+        assertSame(CAUSE, instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertNull(instance.hl7AcknowledgementBytes);
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorEight() throws Exception {
+        instance = new MllpAcknowledgementTimeoutException(ALTERNATE_EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES, CAUSE);
+
+        assertTrue(instance.getMessage().startsWith(ALTERNATE_EXCEPTION_MESSAGE));
+        assertSame(CAUSE, instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.hl7AcknowledgementBytes);
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testGetHl7Acknowledgement() throws Exception {
+        instance = new MllpAcknowledgementTimeoutException(ALTERNATE_EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES, CAUSE);
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.getHl7Acknowledgement());
+
+        instance = new MllpAcknowledgementTimeoutException(ALTERNATE_EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, new byte[0], CAUSE);
+        assertNull(instance.getHl7Acknowledgement());
+
+        instance = new MllpAcknowledgementTimeoutException(ALTERNATE_EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, CAUSE);
+        assertNull(instance.getHl7Acknowledgement());
+    }
+
+}
\ No newline at end of file
diff --git a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpApplicationErrorAcknowledgementExceptionTest.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpApplicationErrorAcknowledgementExceptionTest.java
new file mode 100644
index 0000000..9b58fc4
--- /dev/null
+++ b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpApplicationErrorAcknowledgementExceptionTest.java
@@ -0,0 +1,76 @@
+/**
+ * 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.junit.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for the  class.
+ */
+public class MllpApplicationErrorAcknowledgementExceptionTest extends MllpExceptionTestSupport {
+    MllpApplicationErrorAcknowledgementException instance;
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorOne() throws Exception {
+        instance = new MllpApplicationErrorAcknowledgementException(HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES);
+
+        assertTrue(instance.getMessage().startsWith(MllpApplicationErrorAcknowledgementException.EXCEPTION_MESSAGE));
+        assertNull(instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.getHl7MessageBytes());
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.getHl7AcknowledgementBytes());
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorTwo() throws Exception {
+        instance = new MllpApplicationErrorAcknowledgementException(HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES, CAUSE);
+
+        assertTrue(instance.getMessage().startsWith(MllpApplicationErrorAcknowledgementException.EXCEPTION_MESSAGE));
+        assertSame(CAUSE, instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.getHl7MessageBytes());
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.getHl7AcknowledgementBytes());
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testGetAcknowledgmentType() throws Exception {
+        instance = new MllpApplicationErrorAcknowledgementException(HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES);
+
+        assertEquals("AE", instance.getAcknowledgmentType());
+    }
+
+}
\ No newline at end of file
diff --git a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpApplicationRejectAcknowledgementExceptionTest.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpApplicationRejectAcknowledgementExceptionTest.java
new file mode 100644
index 0000000..3a5994f
--- /dev/null
+++ b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpApplicationRejectAcknowledgementExceptionTest.java
@@ -0,0 +1,76 @@
+/**
+ * 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.junit.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for the  class.
+ */
+public class MllpApplicationRejectAcknowledgementExceptionTest extends MllpExceptionTestSupport {
+    MllpApplicationRejectAcknowledgementException instance;
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorOne() throws Exception {
+        instance = new MllpApplicationRejectAcknowledgementException(HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES);
+
+        assertTrue(instance.getMessage().startsWith(MllpApplicationRejectAcknowledgementException.EXCEPTION_MESSAGE));
+        assertNull(instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.getHl7MessageBytes());
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.getHl7AcknowledgementBytes());
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorTwo() throws Exception {
+        instance = new MllpApplicationRejectAcknowledgementException(HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES, CAUSE);
+
+        assertTrue(instance.getMessage().startsWith(MllpApplicationRejectAcknowledgementException.EXCEPTION_MESSAGE));
+        assertSame(CAUSE, instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.getHl7MessageBytes());
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.getHl7AcknowledgementBytes());
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testGetAcknowledgmentType() throws Exception {
+        instance = new MllpApplicationRejectAcknowledgementException(HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES);
+
+        assertEquals("AR", instance.getAcknowledgmentType());
+    }
+
+}
\ No newline at end of file
diff --git a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpCommitErrorAcknowledgementExceptionTest.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpCommitErrorAcknowledgementExceptionTest.java
new file mode 100644
index 0000000..d929fba
--- /dev/null
+++ b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpCommitErrorAcknowledgementExceptionTest.java
@@ -0,0 +1,76 @@
+/**
+ * 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.junit.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for the  class.
+ */
+public class MllpCommitErrorAcknowledgementExceptionTest extends MllpExceptionTestSupport {
+    MllpCommitErrorAcknowledgementException instance;
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorOne() throws Exception {
+        instance = new MllpCommitErrorAcknowledgementException(HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES);
+
+        assertTrue(instance.getMessage().startsWith(MllpCommitErrorAcknowledgementException.EXCEPTION_MESSAGE));
+        assertNull(instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.getHl7MessageBytes());
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.getHl7AcknowledgementBytes());
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorTwo() throws Exception {
+        instance = new MllpCommitErrorAcknowledgementException(HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES, CAUSE);
+
+        assertTrue(instance.getMessage().startsWith(MllpCommitErrorAcknowledgementException.EXCEPTION_MESSAGE));
+        assertSame(CAUSE, instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.getHl7MessageBytes());
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.getHl7AcknowledgementBytes());
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testGetAcknowledgmentType() throws Exception {
+        instance = new MllpCommitErrorAcknowledgementException(HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES);
+
+        assertEquals("CE", instance.getAcknowledgmentType());
+    }
+
+}
\ No newline at end of file
diff --git a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpCommitRejectAcknowledgementExceptionTest.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpCommitRejectAcknowledgementExceptionTest.java
new file mode 100644
index 0000000..d4dcd30
--- /dev/null
+++ b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpCommitRejectAcknowledgementExceptionTest.java
@@ -0,0 +1,75 @@
+/**
+ * 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.junit.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for the  class.
+ */
+public class MllpCommitRejectAcknowledgementExceptionTest extends MllpExceptionTestSupport {
+    MllpCommitRejectAcknowledgementException instance;
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorOne() throws Exception {
+        instance = new MllpCommitRejectAcknowledgementException(HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES);
+
+        assertTrue(instance.getMessage().startsWith(MllpCommitRejectAcknowledgementException.EXCEPTION_MESSAGE));
+        assertNull(instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.getHl7MessageBytes());
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.getHl7AcknowledgementBytes());
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorTwo() throws Exception {
+        instance = new MllpCommitRejectAcknowledgementException(HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES, CAUSE);
+
+        assertTrue(instance.getMessage().startsWith(MllpCommitRejectAcknowledgementException.EXCEPTION_MESSAGE));
+        assertSame(CAUSE, instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.getHl7MessageBytes());
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.getHl7AcknowledgementBytes());
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testGetAcknowledgmentType() throws Exception {
+        instance = new MllpCommitRejectAcknowledgementException(HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES);
+
+        assertEquals("CR", instance.getAcknowledgmentType());
+    }
+}
\ No newline at end of file
diff --git a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpComponentTest.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpComponentTest.java
new file mode 100644
index 0000000..c3dfc3f
--- /dev/null
+++ b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpComponentTest.java
@@ -0,0 +1,132 @@
+/**
+ * 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.HashMap;
+import java.util.Map;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for the  class.
+ */
+public class MllpComponentTest {
+    Boolean initialLogPhiValue;
+    Integer initialLogPhiMaxBytesValue;
+
+    MllpComponent instance;
+
+    @Before
+    public void setUp() throws Exception {
+        initialLogPhiValue = MllpComponent.logPhi;
+        initialLogPhiMaxBytesValue = MllpComponent.logPhiMaxBytes;
+
+        instance = new MllpComponent();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        MllpComponent.logPhi = initialLogPhiValue;
+        MllpComponent.logPhiMaxBytes = initialLogPhiMaxBytesValue;
+    }
+
+    @Test
+    public void testHasLogPhi() throws Exception {
+        MllpComponent.logPhi = null;
+        assertFalse(MllpComponent.hasLogPhi());
+
+        MllpComponent.logPhi = false;
+        assertTrue(MllpComponent.hasLogPhi());
+
+        MllpComponent.logPhi = true;
+        assertTrue(MllpComponent.hasLogPhi());
+    }
+
+    @Test
+    public void testIsLogPhi() throws Exception {
+        MllpComponent.logPhi = null;
+        assertEquals(MllpComponent.DEFAULT_LOG_PHI, MllpComponent.isLogPhi());
+
+        MllpComponent.logPhi = false;
+        assertFalse(MllpComponent.isLogPhi());
+
+        MllpComponent.logPhi = true;
+        assertTrue(MllpComponent.isLogPhi());
+    }
+
+    @Test
+    public void testSetLogPhi() throws Exception {
+        MllpComponent.setLogPhi(null);
+        assertNull(instance.logPhi);
+
+        MllpComponent.setLogPhi(true);
+        assertEquals(Boolean.TRUE, instance.logPhi);
+
+        MllpComponent.setLogPhi(false);
+        assertEquals(Boolean.FALSE, instance.logPhi);
+    }
+
+
+    @Test
+    public void testHasLogPhiMaxBytes() throws Exception {
+        MllpComponent.logPhiMaxBytes = null;
+        assertFalse(MllpComponent.hasLogPhiMaxBytes());
+
+        MllpComponent.logPhiMaxBytes = -1;
+        assertTrue(MllpComponent.hasLogPhiMaxBytes());
+
+        MllpComponent.logPhiMaxBytes = 1024;
+        assertTrue(MllpComponent.hasLogPhiMaxBytes());
+    }
+
+    @Test
+    public void testGetLogPhiMaxBytes() throws Exception {
+        MllpComponent.logPhiMaxBytes = null;
+        assertEquals(MllpComponent.DEFAULT_LOG_PHI_MAX_BYTES, MllpComponent.getLogPhiMaxBytes());
+
+        int expected = -1;
+        MllpComponent.logPhiMaxBytes = expected;
+        assertEquals(expected, MllpComponent.getLogPhiMaxBytes());
+
+        expected = 1024;
+        MllpComponent.logPhiMaxBytes = expected;
+        assertEquals(expected, MllpComponent.getLogPhiMaxBytes());
+    }
+
+    @Test
+    public void testSetLogPhiMaxBytes() throws Exception {
+        Integer expected = null;
+        MllpComponent.setLogPhiMaxBytes(expected);
+        assertEquals(expected, instance.logPhiMaxBytes);
+
+        expected = -1;
+        MllpComponent.setLogPhiMaxBytes(expected);
+        assertEquals(expected, instance.logPhiMaxBytes);
+
+        expected = 1024;
+        MllpComponent.setLogPhiMaxBytes(expected);
+        assertEquals(expected, instance.logPhiMaxBytes);
+    }
+}
\ No newline at end of file
diff --git a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpTcpServerConsumerBlueprintTest.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpConfigurationBeanBlueprintTest.java
similarity index 55%
copy from components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpTcpServerConsumerBlueprintTest.java
copy to components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpConfigurationBeanBlueprintTest.java
index 9e195f4..6395755 100644
--- a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpTcpServerConsumerBlueprintTest.java
+++ b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpConfigurationBeanBlueprintTest.java
@@ -14,43 +14,26 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.camel.component.mllp;
 
 import java.util.Dictionary;
 import java.util.Map;
-import java.util.Properties;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.camel.EndpointInject;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.impl.DefaultComponentResolver;
 import org.apache.camel.spi.ComponentResolver;
-import org.apache.camel.test.AvailablePortFinder;
 import org.apache.camel.test.blueprint.CamelBlueprintTestSupport;
-import org.apache.camel.test.junit.rule.mllp.MllpClientResource;
 import org.apache.camel.util.KeyValueHolder;
-import org.junit.Rule;
 import org.junit.Test;
 
-import static org.apache.camel.test.mllp.Hl7MessageGenerator.generateMessage;
-
-public class MllpTcpServerConsumerBlueprintTest extends CamelBlueprintTestSupport {
-
-    @Rule
-    public MllpClientResource mllpClient = new MllpClientResource();
-
-    final String receivedUri = "mock://received";
-    final String mllpHost = "localhost";
-
-    @EndpointInject(uri = receivedUri)
+public class MllpConfigurationBeanBlueprintTest extends CamelBlueprintTestSupport {
+    @EndpointInject(uri = "mock://target")
     MockEndpoint received;
 
     @Override
-    protected String getBlueprintDescriptor() {
-        return "OSGI-INF/blueprint/mllp-tcp-server-consumer-test.xml";
-    }
-
-    @Override
     protected void addServicesOnStartup(Map<String, KeyValueHolder<Object, Dictionary>> services) {
         ComponentResolver testResolver = new DefaultComponentResolver();
 
@@ -58,44 +41,19 @@ public class MllpTcpServerConsumerBlueprintTest extends CamelBlueprintTestSuppor
     }
 
     @Override
-    protected Properties useOverridePropertiesWithPropertiesComponent() {
-        mllpClient.setMllpHost(mllpHost);
-        mllpClient.setMllpPort(AvailablePortFinder.getNextAvailable());
-
-        Properties props = new Properties();
-
-        props.setProperty("receivedUri", receivedUri);
-        props.setProperty("mllp.host", mllpClient.getMllpHost());
-        props.setProperty("mllp.port", Integer.toString(mllpClient.getMllpPort()));
-
-        return props;
-    }
-
-    /*
-        This doesn't seem to work
-    @Override
-    protected String useOverridePropertiesWithConfigAdmin(Dictionary props) throws Exception {
-        mllpClient.setMllpPort(AvailablePortFinder.getNextAvailable());
-
-        props.put("mllp.port", mllpClient.getMllpPort() );
-
-        return "MllpTcpServerConsumerBlueprintTest";
+    protected String getBlueprintDescriptor() {
+        return "OSGI-INF/blueprint/mllp-configuration-bean-test.xml";
     }
-    */
 
     @Test
     public void testReceiveMultipleMessages() throws Exception {
         int sendMessageCount = 5;
         received.expectedMinimumMessageCount(5);
 
-        mllpClient.connect();
-
         for (int i = 1; i <= sendMessageCount; ++i) {
-            mllpClient.sendMessageAndWaitForAcknowledgement(generateMessage(i));
+            template.sendBody("direct://source", "Message " + i);
         }
 
-        mllpClient.close();
-
         assertMockEndpointsSatisfied(10, TimeUnit.SECONDS);
     }
 
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
new file mode 100644
index 0000000..7818efb
--- /dev/null
+++ b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpEndpointTest.java
@@ -0,0 +1,195 @@
+/**
+ * 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.nio.charset.StandardCharsets;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+/**
+ * Tests for the MllpEndpoint class.
+ */
+public class MllpEndpointTest {
+    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' + '\n';
+    // @formatter:on
+
+    static final String TEST_MESSAGE = MSH_SEGMENT + '\r' + REMAINING_SEGMENTS;
+
+    MllpEndpoint instance;
+
+    @Before
+    public void setUp() throws Exception {
+        instance = new MllpEndpoint("mllp://dummy", new MllpComponent(), new MllpConfiguration());
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testCreateNewStringWithNullMessage() throws Exception {
+        assertNull(instance.createNewString(null, null));
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testCreateNewStringWithEmptyMessage() throws Exception {
+        byte[] emptyMessage = new byte[0];
+
+        assertEquals("", instance.createNewString(emptyMessage, null));
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testCreateNewStringWithBadMessage() throws Exception {
+        final String testMessage =
+            "MSH|^~\\&" + '\r' + '\n';
+
+        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 testCreateNewStringWithBadCharsetSpecifiedAndNoMsh18InMessage() throws Exception {
+        instance.configuration.charsetName = "BAD-CHARSET";
+
+        assertEquals(TEST_MESSAGE, instance.createNewString(TEST_MESSAGE.getBytes(StandardCharsets.ISO_8859_1), null));
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testCreateNewStringWithCharsetSpecifiedAndNoMsh18InMessage() throws Exception {
+        instance.configuration.charsetName = StandardCharsets.US_ASCII.name();
+
+        assertEquals(TEST_MESSAGE, instance.createNewString(TEST_MESSAGE.getBytes(StandardCharsets.ISO_8859_1), null));
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testCreateNewStringWithoutCharsetSpecifiedAndNoMsh18InMessage() throws Exception {
+        assertEquals(TEST_MESSAGE, instance.createNewString(TEST_MESSAGE.getBytes(StandardCharsets.ISO_8859_1), null));
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testCreateNewStringWithoutCharsetSpecifiedAndMsh18PassedToFunction() throws Exception {
+        final String testMessage = MSH_SEGMENT + "|||||||8859/1" + '\r' + REMAINING_SEGMENTS;
+
+        assertEquals(testMessage, instance.createNewString(testMessage.getBytes(StandardCharsets.ISO_8859_1), "8859/1"));
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testCreateNewStringWithoutCharsetSpecifiedAndNoMsh18PassedToFunction() throws Exception {
+        final String testMessage = MSH_SEGMENT + "|||||||8859/1" + '\r' + REMAINING_SEGMENTS;
+
+        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/MllpExceptionTest.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpExceptionTest.java
index 0c6940b..e09e8b8 100644
--- a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpExceptionTest.java
+++ b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpExceptionTest.java
@@ -14,101 +14,180 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.camel.component.mllp;
 
-import org.junit.After;
+import java.lang.ref.WeakReference;
+
+import org.apache.camel.component.mllp.internal.Hl7Util;
+
+import org.junit.Before;
 import org.junit.Test;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 
-public class MllpExceptionTest {
-    static final String EXCEPTION_MESSAGE = "Test Frame Exception";
+public class MllpExceptionTest extends MllpExceptionTestSupport {
+    static final String EXCEPTION_MESSAGE = "Test MllpException";
+    static final byte[] NULL_BYTE_ARRAY = null;
+    static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
 
-    static final String HL7_MESSAGE =
-            "MSH|^~\\&|APP_A|FAC_A|^org^sys||20161206193919||ADT^A04|00001||2.6" + '\r'
-                    + "PID|1||1100832^^^^PI||TEST^FIG||98765432|U||R|435 MAIN STREET^^LONGMONT^CO^80503||123-456-7890|||S" + '\r'
-                    + '\r' + '\n';
+    MllpException instance;
 
-    static final String HL7_ACK =
-            "MSH|^~\\&|APP_A|FAC_A|^org^sys||20161206193919||ACK^A04|00002||2.6" + '\r'
-                    + "MSA|AA|00001" + '\r'
-                    + '\r' + '\n';
-
-    @After
-    public void tearDown() throws Exception {
-        System.clearProperty(MllpComponent.MLLP_LOG_PHI_PROPERTY);
+    @Before
+    public void setUp() throws Exception {
+        instance = new MllpException(EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES);
     }
 
     @Test
-    public void testLogPhiDefault() throws Exception {
-        assertEquals(expectedMessage(HL7_MESSAGE, HL7_ACK), createException(HL7_MESSAGE, HL7_ACK).getMessage());
+    public void testGetHl7MessageBytes() throws Exception {
+        instance = new MllpException(EXCEPTION_MESSAGE);
+        assertNull(instance.getHl7MessageBytes());
+
+        instance = new MllpException(EXCEPTION_MESSAGE, NULL_BYTE_ARRAY);
+        assertNull(instance.getHl7MessageBytes());
+
+        instance = new MllpException(EXCEPTION_MESSAGE, NULL_BYTE_ARRAY, NULL_BYTE_ARRAY);
+        assertNull(instance.getHl7MessageBytes());
+
+        instance = new MllpException(EXCEPTION_MESSAGE, NULL_BYTE_ARRAY, EMPTY_BYTE_ARRAY);
+        assertNull(instance.getHl7MessageBytes());
+
+        instance = new MllpException(EXCEPTION_MESSAGE, EMPTY_BYTE_ARRAY);
+        assertNull(instance.getHl7MessageBytes());
+
+        instance = new MllpException(EXCEPTION_MESSAGE, EMPTY_BYTE_ARRAY, NULL_BYTE_ARRAY);
+        assertNull(instance.getHl7MessageBytes());
+
+        instance = new MllpException(EXCEPTION_MESSAGE, EMPTY_BYTE_ARRAY, EMPTY_BYTE_ARRAY);
+        assertNull(instance.getHl7MessageBytes());
+
+        instance = new MllpException(EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES);
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.getHl7MessageBytes());
+
+        instance = new MllpException(EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, NULL_BYTE_ARRAY);
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.getHl7MessageBytes());
+
+        instance = new MllpException(EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, EMPTY_BYTE_ARRAY);
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.getHl7MessageBytes());
+
+        instance = new MllpException(EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES);
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.getHl7MessageBytes());
     }
 
     @Test
-    public void testLogPhiDisabled() throws Exception {
-        System.setProperty(MllpComponent.MLLP_LOG_PHI_PROPERTY, "false");
+    public void testGetHl7AcknowledgementBytes() throws Exception {
+        instance = new MllpException(EXCEPTION_MESSAGE);
+        assertNull(instance.getHl7AcknowledgementBytes());
+
+        instance = new MllpException(EXCEPTION_MESSAGE, NULL_BYTE_ARRAY);
+        assertNull(instance.getHl7AcknowledgementBytes());
+
+        instance = new MllpException(EXCEPTION_MESSAGE, NULL_BYTE_ARRAY, NULL_BYTE_ARRAY);
+        assertNull(instance.getHl7AcknowledgementBytes());
+
+        instance = new MllpException(EXCEPTION_MESSAGE, NULL_BYTE_ARRAY, EMPTY_BYTE_ARRAY);
+        assertNull(instance.getHl7AcknowledgementBytes());
+
+        instance = new MllpException(EXCEPTION_MESSAGE, EMPTY_BYTE_ARRAY);
+        assertNull(instance.getHl7AcknowledgementBytes());
+
+        instance = new MllpException(EXCEPTION_MESSAGE, EMPTY_BYTE_ARRAY, NULL_BYTE_ARRAY);
+        assertNull(instance.getHl7AcknowledgementBytes());
+
+        instance = new MllpException(EXCEPTION_MESSAGE, EMPTY_BYTE_ARRAY, EMPTY_BYTE_ARRAY);
+        assertNull(instance.getHl7AcknowledgementBytes());
+
+        instance = new MllpException(EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES);
+        assertNull(instance.getHl7AcknowledgementBytes());
 
-        assertEquals(EXCEPTION_MESSAGE, createException(HL7_MESSAGE, HL7_ACK).getMessage());
+        instance = new MllpException(EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, NULL_BYTE_ARRAY);
+        assertNull(instance.getHl7AcknowledgementBytes());
+
+        instance = new MllpException(EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, EMPTY_BYTE_ARRAY);
+        assertNull(instance.getHl7AcknowledgementBytes());
+
+        instance = new MllpException(EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES);
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.getHl7AcknowledgementBytes());
+
+        instance = new MllpException(EXCEPTION_MESSAGE, null, HL7_ACKNOWLEDGEMENT_BYTES);
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.getHl7AcknowledgementBytes());
+
+        instance = new MllpException(EXCEPTION_MESSAGE, EMPTY_BYTE_ARRAY, HL7_ACKNOWLEDGEMENT_BYTES);
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.getHl7AcknowledgementBytes());
+
+        instance = new MllpException(EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES);
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.getHl7AcknowledgementBytes());
     }
 
     @Test
-    public void testLogPhiEnabled() throws Exception {
+    public void testNullHl7Message() throws Exception {
         System.setProperty(MllpComponent.MLLP_LOG_PHI_PROPERTY, "true");
 
-        assertEquals(expectedMessage(HL7_MESSAGE, HL7_ACK), createException(HL7_MESSAGE, HL7_ACK).getMessage());
+        instance = new MllpException(EXCEPTION_MESSAGE, null, HL7_ACKNOWLEDGEMENT_BYTES);
+
+        assertEquals(expectedMessage(null, HL7_ACKNOWLEDGEMENT), instance.getMessage());
     }
 
     @Test
-    public void testNullHl7Message() throws Exception {
+    public void testEmptyHl7Message() throws Exception {
         System.setProperty(MllpComponent.MLLP_LOG_PHI_PROPERTY, "true");
 
-        assertEquals(expectedMessage(null, HL7_ACK), createException(null, HL7_ACK).getMessage());
+        instance = new MllpException(EXCEPTION_MESSAGE, EMPTY_BYTE_ARRAY, HL7_ACKNOWLEDGEMENT_BYTES);
+
+        assertEquals(expectedMessage(null, HL7_ACKNOWLEDGEMENT), instance.getMessage());
     }
 
     @Test
     public void testNullHl7Acknowledgement() throws Exception {
         System.setProperty(MllpComponent.MLLP_LOG_PHI_PROPERTY, "true");
 
-        assertEquals(expectedMessage(HL7_MESSAGE, null), createException(HL7_MESSAGE, null).getMessage());
+        instance = new MllpException(EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, NULL_BYTE_ARRAY);
+
+        assertEquals(expectedMessage(HL7_MESSAGE, null), instance.getMessage());
     }
 
     @Test
-    public void testNullHl7Payloads() throws Exception {
+    public void testEmptyAcknowledgement() throws Exception {
         System.setProperty(MllpComponent.MLLP_LOG_PHI_PROPERTY, "true");
 
-        assertEquals(expectedMessage(null, null), createException(null, null).getMessage());
-    }
+        instance = new MllpException(EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, EMPTY_BYTE_ARRAY);
 
+        assertEquals(expectedMessage(HL7_MESSAGE, null), instance.getMessage());
+    }
 
-    // Utility methods
-    private Exception createException(String hl7Message, String hl7Acknowledgment) {
-        byte[] hl7MessageBytes = null;
-        byte[] hl7AcknowledgementBytes = null;
+    @Test
+    public void testNullHl7Payloads() throws Exception {
+        System.setProperty(MllpComponent.MLLP_LOG_PHI_PROPERTY, "true");
 
-        if (hl7Message != null) {
-            hl7MessageBytes = hl7Message.getBytes();
-        }
+        instance = new MllpException(EXCEPTION_MESSAGE, NULL_BYTE_ARRAY, NULL_BYTE_ARRAY);
 
-        if (hl7Acknowledgment != null) {
-            hl7AcknowledgementBytes = hl7Acknowledgment.getBytes();
-        }
-        return new MllpException(EXCEPTION_MESSAGE, hl7MessageBytes, hl7AcknowledgementBytes);
+        assertEquals(expectedMessage(null, null), instance.getMessage());
     }
 
+
     private String expectedMessage(String hl7Message, String hl7Acknowledgment) {
-        final String exceptionMessageFormat = EXCEPTION_MESSAGE + " \n\t{hl7Message= %s} \n\t{hl7Acknowledgement= %s}";
+        StringBuilder expectedMessageBuilder = new StringBuilder();
 
-        String formattedHl7Message = null;
-        String formattedHl7Acknowledgement = null;
+        expectedMessageBuilder.append(EXCEPTION_MESSAGE);
 
         if (hl7Message != null) {
-            formattedHl7Message = hl7Message.replaceAll("\r", "<CR>").replaceAll("\n", "<LF>");
+            expectedMessageBuilder.append("\n\t{hl7Message [")
+                .append(hl7Message.length())
+                .append("] = ")
+                .append(hl7Message.replaceAll("\r", "<CR>").replaceAll("\n", "<LF>"))
+                .append("}");
         }
 
         if (hl7Acknowledgment != null) {
-            formattedHl7Acknowledgement = hl7Acknowledgment.replaceAll("\r", "<CR>").replaceAll("\n", "<LF>");
+            expectedMessageBuilder.append("\n\t{hl7Acknowledgement [")
+                .append(hl7Acknowledgment.length())
+                .append("] = ")
+                .append(hl7Acknowledgment.replaceAll("\r", "<CR>").replaceAll("\n", "<LF>"))
+                .append("}");
         }
 
-        return String.format(exceptionMessageFormat, formattedHl7Message, formattedHl7Acknowledgement);
+        return expectedMessageBuilder.toString();
     }
 }
\ No newline at end of file
diff --git a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpExceptionTestSupport.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpExceptionTestSupport.java
new file mode 100644
index 0000000..5ae5332
--- /dev/null
+++ b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpExceptionTestSupport.java
@@ -0,0 +1,44 @@
+/**
+ * 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.junit.After;
+
+public class MllpExceptionTestSupport {
+    public static final String HL7_MESSAGE =
+        "MSH|^~\\&|APP_A|FAC_A|^org^sys||20161206193919||ADT^A04|00001||2.6" + '\r'
+            + "PID|1||1100832^^^^PI||TEST^FIG||98765432|U||R|435 MAIN STREET^^LONGMONT^CO^80503||123-456-7890|||S" + '\r'
+            + '\n';
+
+    public static final String HL7_ACKNOWLEDGEMENT =
+        "MSH|^~\\&|APP_A|FAC_A|^org^sys||20161206193919||ACK^A04|00002||2.6" + '\r'
+            + "MSA|AA|00001" + '\r'
+            + '\n';
+
+    public static final byte[] HL7_MESSAGE_BYTES = HL7_MESSAGE.getBytes();
+    public static final byte[] HL7_ACKNOWLEDGEMENT_BYTES = HL7_ACKNOWLEDGEMENT.getBytes();
+
+    public static final Exception CAUSE = new Exception("Dummy Exception");
+
+    @After
+    public void tearDown() throws Exception {
+        System.clearProperty(MllpComponent.MLLP_LOG_PHI_PROPERTY);
+        System.clearProperty(MllpComponent.MLLP_LOG_PHI_MAX_BYTES_PROPERTY);
+    }
+
+}
\ No newline at end of file
diff --git a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpInvalidAcknowledgementExceptionTest.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpInvalidAcknowledgementExceptionTest.java
new file mode 100644
index 0000000..4a4c0c2
--- /dev/null
+++ b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpInvalidAcknowledgementExceptionTest.java
@@ -0,0 +1,64 @@
+/**
+ * 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.junit.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for the  class.
+ */
+public class MllpInvalidAcknowledgementExceptionTest extends MllpExceptionTestSupport {
+    static final String EXCEPTION_MESSAGE = "Invalid Acknowledgment Message";
+
+    MllpInvalidAcknowledgementException instance;
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorOne() throws Exception {
+        instance = new MllpInvalidAcknowledgementException(EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES);
+
+        assertTrue(instance.getMessage().startsWith(EXCEPTION_MESSAGE));
+        assertNull(instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.getHl7MessageBytes());
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.getHl7AcknowledgementBytes());
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorTwo() throws Exception {
+        instance = new MllpInvalidAcknowledgementException(EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES, CAUSE);
+
+        assertTrue(instance.getMessage().startsWith(EXCEPTION_MESSAGE));
+        assertSame(CAUSE, instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.getHl7MessageBytes());
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.getHl7AcknowledgementBytes());
+    }
+}
\ No newline at end of file
diff --git a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpInvalidMessageExceptionTest.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpInvalidMessageExceptionTest.java
new file mode 100644
index 0000000..3a58774
--- /dev/null
+++ b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpInvalidMessageExceptionTest.java
@@ -0,0 +1,64 @@
+/**
+ * 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.junit.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for the  class.
+ */
+public class MllpInvalidMessageExceptionTest extends MllpExceptionTestSupport {
+    static final String EXCEPTION_MESSAGE = "Invalid Message Exception Message";
+
+    MllpInvalidMessageException instance;
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorOne() throws Exception {
+        instance = new MllpInvalidMessageException(EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES);
+
+        assertTrue(instance.getMessage().startsWith(EXCEPTION_MESSAGE));
+        assertNull(instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.getHl7MessageBytes());
+        assertNull(instance.getHl7AcknowledgementBytes());
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorTwo() throws Exception {
+        instance = new MllpInvalidMessageException(EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, CAUSE);
+
+        assertTrue(instance.getMessage().startsWith(EXCEPTION_MESSAGE));
+        assertSame(CAUSE, instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.getHl7MessageBytes());
+        assertNull(instance.getHl7AcknowledgementBytes());
+    }
+}
\ No newline at end of file
diff --git a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpNegativeAcknowledgementExceptionTest.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpNegativeAcknowledgementExceptionTest.java
new file mode 100644
index 0000000..a70ab75
--- /dev/null
+++ b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpNegativeAcknowledgementExceptionTest.java
@@ -0,0 +1,93 @@
+/**
+ * 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.junit.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for the  class.
+ */
+public class MllpNegativeAcknowledgementExceptionTest extends MllpExceptionTestSupport {
+    static final String EXCEPTION_MESSAGE = "Negative Acknowledgement";
+
+    MllpNegativeAcknowledgementException instance;
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorOne() throws Exception {
+        instance = new MllpNegativeAcknowledgementExceptionStub(EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES);
+
+        assertTrue(instance.getMessage().startsWith(EXCEPTION_MESSAGE));
+        assertNull(instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.getHl7MessageBytes());
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.getHl7AcknowledgementBytes());
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorTwo() throws Exception {
+        instance = new MllpNegativeAcknowledgementExceptionStub(EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES, CAUSE);
+
+        assertTrue(instance.getMessage().startsWith(EXCEPTION_MESSAGE));
+        assertSame(CAUSE, instance.getCause());
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.getHl7MessageBytes());
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.getHl7AcknowledgementBytes());
+    }
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testGetAcknowledgmentType() throws Exception {
+        instance = new MllpNegativeAcknowledgementExceptionStub(EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES);
+
+        assertNull(instance.getAcknowledgmentType());
+    }
+
+    static class MllpNegativeAcknowledgementExceptionStub extends MllpNegativeAcknowledgementException {
+
+        MllpNegativeAcknowledgementExceptionStub(String message, byte[] hl7Message, byte[] hl7Acknowledgement) {
+            super(message, hl7Message, hl7Acknowledgement);
+        }
+
+        MllpNegativeAcknowledgementExceptionStub(String message, byte[] hl7Message, byte[] hl7Acknowledgement, Throwable cause) {
+            super(message, hl7Message, hl7Acknowledgement, cause);
+        }
+
+        @Override
+        public String getAcknowledgmentType() {
+            return null;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpProducerConsumerLoopbackTest.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpProducerConsumerLoopbackTest.java
index e090a50..e547f40 100644
--- a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpProducerConsumerLoopbackTest.java
+++ b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpProducerConsumerLoopbackTest.java
@@ -14,6 +14,7 @@
  * 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;
@@ -25,17 +26,21 @@ 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.junit4.CamelTestSupport;
+
+import org.apache.camel.test.mllp.Hl7TestMessageGenerator;
 import org.apache.camel.test.mllp.PassthroughProcessor;
+
 import org.hamcrest.CoreMatchers;
 import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
-import static org.apache.camel.test.mllp.Hl7MessageGenerator.generateMessage;
 import static org.junit.Assume.assumeTrue;
 
+
 public class MllpProducerConsumerLoopbackTest extends CamelTestSupport {
     int mllpPort = AvailablePortFinder.getNextAvailable();
     String mllpHost = "localhost";
@@ -46,6 +51,11 @@ public class MllpProducerConsumerLoopbackTest extends CamelTestSupport {
     @EndpointInject(uri = "mock://acknowledged")
     MockEndpoint acknowledged;
 
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        assumeTrue("Skipping test running in CI server - Fails sometimes on CI server with address already in use", System.getenv("BUILD_ID") == null);
+    }
+
     @Override
     protected CamelContext createCamelContext() throws Exception {
         DefaultCamelContext context = (DefaultCamelContext) super.createCamelContext();
@@ -56,11 +66,6 @@ public class MllpProducerConsumerLoopbackTest extends CamelTestSupport {
         return context;
     }
 
-    @BeforeClass
-    public static void setUpClass() throws Exception {
-        assumeTrue("Skipping test running in CI server - Fails sometimes on CI server with address already in use", System.getenv("BUILD_ID") == null);
-    }
-
     @Override
     protected RouteBuilder[] createRouteBuilders() throws Exception {
         RouteBuilder[] builders = new RouteBuilder[2];
@@ -69,11 +74,11 @@ public class MllpProducerConsumerLoopbackTest extends CamelTestSupport {
             String routeId = "mllp-receiver";
 
             public void configure() {
-                fromF("mllp://%s:%d?autoAck=true&readTimeout=1000", mllpHost, mllpPort)
-                        .convertBodyTo(String.class)
-                        .to(acknowledged)
-                        .process(new PassthroughProcessor("after send to result"))
-                        .log(LoggingLevel.INFO, routeId, "Receiving: ${body}");
+                fromF("mllp://%s:%d?autoAck=true&readTimeout=5000", mllpHost, mllpPort).id(routeId)
+                    .convertBodyTo(String.class)
+                    .to(acknowledged)
+                    .process(new PassthroughProcessor("after send to result"))
+                    .log(LoggingLevel.DEBUG, routeId, "Receiving: ${body}");
             }
         };
 
@@ -82,9 +87,9 @@ public class MllpProducerConsumerLoopbackTest extends CamelTestSupport {
 
             public void configure() {
                 from(source.getDefaultEndpoint()).routeId(routeId)
-                        .log(LoggingLevel.INFO, routeId, "Sending: ${body}")
-                        .toF("mllp://%s:%d?readTimeout=5000", mllpHost, mllpPort)
-                        .setBody(header(MllpConstants.MLLP_ACKNOWLEDGEMENT));
+                    .log(LoggingLevel.DEBUG, routeId, "Sending: ${body}")
+                    .toF("mllp://%s:%d?readTimeout=5000", mllpHost, mllpPort)
+                    .setBody(header(MllpConstants.MLLP_ACKNOWLEDGEMENT));
             }
         };
 
@@ -93,7 +98,7 @@ public class MllpProducerConsumerLoopbackTest extends CamelTestSupport {
 
     @Test
     public void testLoopbackWithOneMessage() throws Exception {
-        String testMessage = generateMessage();
+        String testMessage = Hl7TestMessageGenerator.generateMessage();
         acknowledged.expectedBodiesReceived(testMessage);
 
         String acknowledgement = source.requestBody((Object) testMessage, String.class);
@@ -108,11 +113,11 @@ public class MllpProducerConsumerLoopbackTest extends CamelTestSupport {
         acknowledged.expectedMessageCount(messageCount);
 
         for (int i = 1; i <= messageCount; ++i) {
-            String testMessage = generateMessage(i);
+            log.debug("Processing message {}", i);
+            String testMessage = Hl7TestMessageGenerator.generateMessage(i);
             acknowledged.message(i - 1).body().isEqualTo(testMessage);
             String acknowledgement = source.requestBody((Object) testMessage, String.class);
             Assert.assertThat("Should be acknowledgment for message " + i, acknowledgement, CoreMatchers.containsString(String.format("MSA|AA|%05d", i)));
-
         }
 
         assertMockEndpointsSatisfied(60, TimeUnit.SECONDS);
diff --git a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpReceiveExceptionTest.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpReceiveExceptionTest.java
new file mode 100644
index 0000000..1f0a597
--- /dev/null
+++ b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpReceiveExceptionTest.java
@@ -0,0 +1,129 @@
+/**
+ * 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.junit.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for the  class.
+ */
+public class MllpReceiveExceptionTest extends MllpExceptionTestSupport {
+    static final String TEST_EXCEPTION_MESSAGE = "Receive Exception Message";
+
+    MllpReceiveException instance;
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorOne() throws Exception {
+        instance = new MllpReceiveException(TEST_EXCEPTION_MESSAGE);
+
+        assertNull(instance.getCause());
+        assertTrue(instance.getMessage().startsWith(TEST_EXCEPTION_MESSAGE));
+        assertNull(instance.hl7MessageBytes);
+        assertNull(instance.hl7AcknowledgementBytes);
+    }
+
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorTwo() throws Exception {
+        instance = new MllpReceiveException(TEST_EXCEPTION_MESSAGE, CAUSE);
+
+        assertSame(CAUSE, instance.getCause());
+        assertTrue(instance.getMessage().startsWith(TEST_EXCEPTION_MESSAGE));
+        assertNull(instance.hl7MessageBytes);
+        assertNull(instance.hl7AcknowledgementBytes);
+    }
+
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorThree() throws Exception {
+        instance = new MllpReceiveException(TEST_EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES);
+
+        assertNull(instance.getCause());
+        assertTrue(instance.getMessage().startsWith(TEST_EXCEPTION_MESSAGE));
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertNull(instance.hl7AcknowledgementBytes);
+    }
+
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorFour() throws Exception {
+        instance = new MllpReceiveException(TEST_EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES);
+
+        assertNull(instance.getCause());
+        assertTrue(instance.getMessage().startsWith(TEST_EXCEPTION_MESSAGE));
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.hl7AcknowledgementBytes);
+    }
+
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorFive() throws Exception {
+        instance = new MllpReceiveException(TEST_EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, CAUSE);
+
+        assertSame(CAUSE, instance.getCause());
+        assertTrue(instance.getMessage().startsWith(TEST_EXCEPTION_MESSAGE));
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertNull(instance.hl7AcknowledgementBytes);
+    }
+
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorSix() throws Exception {
+        instance = new MllpReceiveException(TEST_EXCEPTION_MESSAGE, HL7_MESSAGE_BYTES, HL7_ACKNOWLEDGEMENT_BYTES, CAUSE);
+
+        assertSame(CAUSE, instance.getCause());
+        assertTrue(instance.getMessage().startsWith(TEST_EXCEPTION_MESSAGE));
+        assertArrayEquals(HL7_MESSAGE_BYTES, instance.hl7MessageBytes);
+        assertArrayEquals(HL7_ACKNOWLEDGEMENT_BYTES, instance.hl7AcknowledgementBytes);
+    }
+}
\ No newline at end of file
diff --git a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpSocketExceptionTest.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpSocketExceptionTest.java
new file mode 100644
index 0000000..fb5f6c2
--- /dev/null
+++ b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpSocketExceptionTest.java
@@ -0,0 +1,61 @@
+/**
+ * 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.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+/**
+ * Tests for the  class.
+ */
+public class MllpSocketExceptionTest extends MllpExceptionTestSupport {
+    static final String TEST_EXCEPTION_MESSAGE = "MLLP Socket Exception Message";
+
+    MllpSocketException instance;
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorOne() throws Exception {
+        instance = new MllpSocketException(TEST_EXCEPTION_MESSAGE);
+
+        assertNull(instance.getCause());
+        assertEquals(TEST_EXCEPTION_MESSAGE, instance.getMessage());
+    }
+
+
+    /**
+     * Description of test.
+     *
+     * @throws Exception in the event of a test error.
+     */
+    @Test
+    public void testConstructorTwo() throws Exception {
+        instance = new MllpSocketException(TEST_EXCEPTION_MESSAGE, CAUSE);
+
+        assertNotNull(instance.getCause());
+        assertEquals(TEST_EXCEPTION_MESSAGE, instance.getMessage());
+    }
+
+}
\ No newline at end of file
diff --git a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpTcpClientProducerAcknowledgementTest.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpTcpClientProducerAcknowledgementTest.java
deleted file mode 100644
index f6df61c..0000000
--- a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpTcpClientProducerAcknowledgementTest.java
+++ /dev/null
@@ -1,286 +0,0 @@
-/**
- * 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.LoggingLevel;
-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.junit.rule.mllp.MllpServerResource;
-import org.apache.camel.test.junit4.CamelTestSupport;
-
-import org.junit.Rule;
-import org.junit.Test;
-
-import static org.apache.camel.component.mllp.MllpEndpoint.END_OF_BLOCK;
-import static org.apache.camel.component.mllp.MllpEndpoint.START_OF_BLOCK;
-
-public class MllpTcpClientProducerAcknowledgementTest extends CamelTestSupport {
-    static final String TEST_MESSAGE =
-        "MSH|^~\\&|ADT|EPIC|JCAPS|CC|20161206193919|RISTECH|ADT^A08|00001|D|2.3^^|||||||" + '\r'
-            + "EVN|A08|20150107161440||REG_UPDATE_SEND_VISIT_MESSAGES_ON_PATIENT_CHANGES|RISTECH^RADIOLOGY^TECHNOLOGIST^^^^^^UCLA^^^^^RRMC||" + '\r'
-            + "PID|1|2100355^^^MRN^MRN|2100355^^^MRN^MRN||MDCLS9^MC9||19700109|F||U|111 HOVER STREET^^LOS ANGELES^CA^90032^USA^P^^LOS ANGELE|LOS ANGELE|"
-                + "(310)725-6952^P^PH^^^310^7256952||ENGLISH|U||60000013647|565-33-2222|||U||||||||N||" + '\r'
-            + "PD1|||UCLA HEALTH SYSTEM^^10|10002116^ADAMS^JOHN^D^^^^^EPIC^^^^PROVID||||||||||||||" + '\r'
-            + "NK1|1|DOE^MC9^^|OTH|^^^^^USA|(310)888-9999^^^^^310^8889999|(310)999-2222^^^^^310^9992222|Emergency Contact 1|||||||||||||||||||||||||||" + '\r'
-            + "PV1|1|OUTPATIENT|RR CT^^^1000^^^^^^^DEPID|EL|||017511^TOBIAS^JONATHAN^^^^^^EPIC^^^^PROVID|017511^TOBIAS^JONATHAN^^^^^^EPIC^^^^PROVID||||||"
-                + "CLR|||||60000013647|SELF|||||||||||||||||||||HOV_CONF|^^^1000^^^^^^^||20150107161438||||||||||" + '\r'
-            + "PV2||||||||20150107161438||||CT BRAIN W WO CONTRAST||||||||||N|||||||||||||||||||||||||||" + '\r'
-            + "ZPV||||||||||||20150107161438|||||||||" + '\r'
-            + "AL1|1||33361^NO KNOWN ALLERGIES^^NOTCOMPUTRITION^NO KNOWN ALLERGIES^EXTELG||||||" + '\r'
-            + "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';
-
-    static final String EXPECTED_AA =
-        "MSH|^~\\&|JCAPS|CC|ADT|EPIC|20161206193919|RISTECH|ACK^A08|00001|D|2.3^^|||||||" + '\r'
-            + "MSA|AA|00001|" + '\r'
-            + '\n';
-
-    static final String EXPECTED_AR =
-        "MSH|^~\\&|JCAPS|CC|ADT|EPIC|20161206193919|RISTECH|ACK^A08|00001|D|2.3^^|||||||" + '\r'
-            + "MSA|AR|00001|" + '\r'
-            + '\n';
-
-    static final String EXPECTED_AE =
-        "MSH|^~\\&|JCAPS|CC|ADT|EPIC|20161206193919|RISTECH|ACK^A08|00001|D|2.3^^|||||||" + '\r'
-            + "MSA|AE|00001|" + '\r'
-            + '\n';
-
-    @Rule
-    public MllpServerResource mllpServer = new MllpServerResource("localhost", AvailablePortFinder.getNextAvailable());
-
-    @EndpointInject(uri = "direct://source")
-    ProducerTemplate source;
-
-    @EndpointInject(uri = "mock://failed")
-    MockEndpoint failed;
-
-    @EndpointInject(uri = "mock://aa-ack")
-    MockEndpoint aa;
-    @EndpointInject(uri = "mock://ae-nack")
-    MockEndpoint ae;
-    @EndpointInject(uri = "mock://ar-nack")
-    MockEndpoint ar;
-
-    @EndpointInject(uri = "mock://invalid-ack")
-    MockEndpoint invalid;
-
-    @Override
-    protected CamelContext createCamelContext() throws Exception {
-        DefaultCamelContext context = (DefaultCamelContext) super.createCamelContext();
-
-        context.setUseMDCLogging(true);
-        context.setName(this.getClass().getSimpleName());
-
-        return context;
-    }
-
-    @Override
-    protected RouteBuilder createRouteBuilder() throws Exception {
-        return new RouteBuilder() {
-            String routeId = "mllp-sender";
-
-            public void configure() {
-                onException(MllpApplicationRejectAcknowledgementException.class)
-                        .handled(true)
-                        .to(ar)
-                        .log(LoggingLevel.ERROR, routeId, "AR Acknowledgement");
-
-                onException(MllpApplicationErrorAcknowledgementException.class)
-                        .handled(true)
-                        .to(ae)
-                        .log(LoggingLevel.ERROR, routeId, "AE Acknowledgement");
-
-                onException(MllpInvalidAcknowledgementException.class)
-                        .handled(true)
-                        .to(invalid)
-                        .log(LoggingLevel.ERROR, routeId, "Invalid Acknowledgement");
-
-                onCompletion()
-                        .onFailureOnly()
-                        .to(failed)
-                        .log(LoggingLevel.DEBUG, routeId, "Exchange failed");
-
-                from(source.getDefaultEndpoint()).routeId(routeId)
-                        .log(LoggingLevel.INFO, routeId, "Sending Message")
-                        .toF("mllp://%s:%d", mllpServer.getListenHost(), mllpServer.getListenPort())
-                        .log(LoggingLevel.INFO, routeId, "Received Acknowledgement")
-                        .to(aa);
-            }
-        };
-    }
-
-    @Test
-    public void testApplicationAcceptAcknowledgement() throws Exception {
-        aa.expectedBodiesReceived(TEST_MESSAGE);
-        aa.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_TYPE, "AA");
-        aa.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT, EXPECTED_AA.getBytes());
-        aa.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING, EXPECTED_AA);
-
-        failed.expectedMessageCount(0);
-        failed.setAssertPeriod(1000);
-
-        ae.expectedMessageCount(0);
-        ar.expectedMessageCount(0);
-        invalid.expectedMessageCount(0);
-
-        source.sendBody(TEST_MESSAGE);
-
-        assertMockEndpointsSatisfied(15, TimeUnit.SECONDS);
-    }
-
-    @Test
-    public void testApplicationRejectAcknowledgement() throws Exception {
-        ar.expectedBodiesReceived(TEST_MESSAGE);
-        aa.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_TYPE, "AR");
-        ar.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT, EXPECTED_AR.getBytes());
-        ar.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING, EXPECTED_AR);
-
-        failed.expectedMessageCount(0);
-        failed.setAssertPeriod(1000);
-
-        aa.expectedMessageCount(0);
-        ae.expectedMessageCount(0);
-        invalid.expectedMessageCount(0);
-
-        mllpServer.setSendApplicationRejectAcknowledgementModulus(1);
-
-        source.sendBody(TEST_MESSAGE);
-
-        assertMockEndpointsSatisfied(15, TimeUnit.SECONDS);
-    }
-
-    @Test
-    public void testApplicationErrorAcknowledgement() throws Exception {
-        ae.expectedBodiesReceived(TEST_MESSAGE);
-        aa.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_TYPE, "AE");
-        ae.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT, EXPECTED_AE.getBytes());
-        ae.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING, EXPECTED_AE);
-
-        failed.expectedMessageCount(0);
-        failed.setAssertPeriod(1000);
-
-        aa.expectedMessageCount(0);
-        ar.expectedMessageCount(0);
-        invalid.expectedMessageCount(0);
-
-        mllpServer.setSendApplicationErrorAcknowledgementModulus(1);
-
-        source.sendBody(TEST_MESSAGE);
-
-        assertMockEndpointsSatisfied(15, TimeUnit.SECONDS);
-    }
-
-    @Test
-    public void testEmptyAcknowledgement() throws Exception {
-        aa.expectedBodiesReceived(TEST_MESSAGE);
-        aa.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_TYPE, "");
-        aa.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT, "".getBytes());
-        aa.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING, "");
-
-        failed.expectedMessageCount(0);
-        failed.setAssertPeriod(1000);
-
-        ar.expectedMessageCount(0);
-        ae.expectedMessageCount(0);
-        invalid.expectedMessageCount(0);
-
-        mllpServer.setExcludeAcknowledgementModulus(1);
-
-        source.sendBody(TEST_MESSAGE);
-
-        assertMockEndpointsSatisfied(15, TimeUnit.SECONDS);
-    }
-
-    @Test
-    public void testInvalidAcknowledgement() throws Exception {
-        final String badAcknowledgement = "A VERY BAD ACKNOWLEDGEMENT";
-
-        aa.expectedBodiesReceived(TEST_MESSAGE);
-        aa.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_TYPE, "");
-        aa.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING, badAcknowledgement.getBytes());
-        aa.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING, badAcknowledgement);
-
-        failed.expectedMessageCount(0);
-        failed.setAssertPeriod(1000);
-
-        ar.expectedMessageCount(0);
-        ae.expectedMessageCount(0);
-        invalid.expectedMessageCount(0);
-
-        mllpServer.setAcknowledgementString(badAcknowledgement);
-
-        source.sendBody(TEST_MESSAGE);
-
-        assertMockEndpointsSatisfied(15, TimeUnit.SECONDS);
-    }
-
-    @Test
-    public void testInvalidAcknowledgementContainingEmbeddedStartOfBlock() throws Exception {
-        final String badAcknowledgement = EXPECTED_AA.replaceFirst("RISTECH", "RISTECH" + START_OF_BLOCK);
-
-        aa.expectedBodiesReceived(TEST_MESSAGE);
-        aa.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_TYPE, "AA");
-        aa.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING, badAcknowledgement.getBytes());
-        aa.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING, badAcknowledgement);
-
-        failed.expectedMessageCount(0);
-        failed.setAssertPeriod(1000);
-
-        ar.expectedMessageCount(0);
-        ae.expectedMessageCount(0);
-        invalid.expectedMessageCount(0);
-
-        mllpServer.setAcknowledgementString(badAcknowledgement);
-
-        source.sendBody(TEST_MESSAGE);
-
-        assertMockEndpointsSatisfied(15, TimeUnit.SECONDS);
-    }
-
-    @Test
-    public void testInvalidAcknowledgementContainingEmbeddedEndOfBlock() throws Exception {
-        final String badAcknowledgement = EXPECTED_AA.replaceFirst("RISTECH", "RISTECH" + END_OF_BLOCK);
-
-        aa.expectedBodiesReceived(TEST_MESSAGE);
-        aa.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_TYPE, "AA");
-        aa.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING, badAcknowledgement.getBytes());
-        aa.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING, badAcknowledgement);
-
-        failed.expectedMessageCount(0);
-        failed.setAssertPeriod(1000);
-
-        ar.expectedMessageCount(0);
-        ae.expectedMessageCount(0);
-        invalid.expectedMessageCount(0);
-
-        mllpServer.setAcknowledgementString(badAcknowledgement);
-
-        source.sendBody(TEST_MESSAGE);
-
-        assertMockEndpointsSatisfied(15, TimeUnit.SECONDS);
-    }
-}
diff --git a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpTcpClientProducerAcknowledgementValidationTest.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpTcpClientProducerAcknowledgementValidationTest.java
deleted file mode 100644
index 1ff641c..0000000
--- a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpTcpClientProducerAcknowledgementValidationTest.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/**
- * 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.LoggingLevel;
-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.junit.rule.mllp.MllpServerResource;
-import org.apache.camel.test.junit4.CamelTestSupport;
-import org.junit.Rule;
-import org.junit.Test;
-
-import static org.apache.camel.component.mllp.MllpEndpoint.END_OF_BLOCK;
-import static org.apache.camel.component.mllp.MllpEndpoint.START_OF_BLOCK;
-
-public class MllpTcpClientProducerAcknowledgementValidationTest extends CamelTestSupport {
-    static final String TEST_MESSAGE =
-        "MSH|^~\\&|ADT|EPIC|JCAPS|CC|20161206193919|RISTECH|ADT^A08|00001|D|2.3^^|||||||" + '\r'
-            + "EVN|A08|20150107161440||REG_UPDATE_SEND_VISIT_MESSAGES_ON_PATIENT_CHANGES|RISTECH^RADIOLOGY^TECHNOLOGIST^^^^^^UCLA^^^^^RRMC||" + '\r'
-            + "PID|1|2100355^^^MRN^MRN|2100355^^^MRN^MRN||MDCLS9^MC9||19700109|F||U|111 HOVER STREET^^LOS ANGELES^CA^90032^USA^P^^LOS ANGELE|LOS ANGELE|"
-                + "(310)725-6952^P^PH^^^310^7256952||ENGLISH|U||60000013647|565-33-2222|||U||||||||N||" + '\r'
-            + "PD1|||UCLA HEALTH SYSTEM^^10|10002116^ADAMS^JOHN^D^^^^^EPIC^^^^PROVID||||||||||||||" + '\r'
-            + "NK1|1|DOE^MC9^^|OTH|^^^^^USA|(310)888-9999^^^^^310^8889999|(310)999-2222^^^^^310^9992222|Emergency Contact 1|||||||||||||||||||||||||||" + '\r'
-            + "PV1|1|OUTPATIENT|RR CT^^^1000^^^^^^^DEPID|EL|||017511^TOBIAS^JONATHAN^^^^^^EPIC^^^^PROVID|017511^TOBIAS^JONATHAN^^^^^^EPIC^^^^PROVID||||||"
-                + "CLR|||||60000013647|SELF|||||||||||||||||||||HOV_CONF|^^^1000^^^^^^^||20150107161438||||||||||" + '\r'
-            + "PV2||||||||20150107161438||||CT BRAIN W WO CONTRAST||||||||||N|||||||||||||||||||||||||||" + '\r'
-            + "ZPV||||||||||||20150107161438|||||||||" + '\r'
-            + "AL1|1||33361^NO KNOWN ALLERGIES^^NOTCOMPUTRITION^NO KNOWN ALLERGIES^EXTELG||||||" + '\r'
-            + "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';
-
-    static final String EXPECTED_AA =
-        "MSH|^~\\&|JCAPS|CC|ADT|EPIC|20161206193919|RISTECH|ACK^A08|00001|D|2.3^^|||||||" + '\r'
-            + "MSA|AA|00001|" + '\r'
-            + '\n';
-
-    static final String EXPECTED_AR =
-        "MSH|^~\\&|JCAPS|CC|ADT|EPIC|20161206193919|RISTECH|ACK^A08|00001|D|2.3^^|||||||" + '\r'
-            + "MSA|AR|00001|" + '\r'
-            + '\n';
-
-    static final String EXPECTED_AE =
-        "MSH|^~\\&|JCAPS|CC|ADT|EPIC|20161206193919|RISTECH|ACK^A08|00001|D|2.3^^|||||||" + '\r'
-            + "MSA|AE|00001|" + '\r'
-            + '\n';
-
-    @Rule
-    public MllpServerResource mllpServer = new MllpServerResource("localhost", AvailablePortFinder.getNextAvailable());
-
-    @EndpointInject(uri = "direct://source")
-    ProducerTemplate source;
-
-    @EndpointInject(uri = "mock://failed")
-    MockEndpoint failed;
-
-    @EndpointInject(uri = "mock://aa-ack")
-    MockEndpoint aa;
-    @EndpointInject(uri = "mock://ae-nack")
-    MockEndpoint ae;
-    @EndpointInject(uri = "mock://ar-nack")
-    MockEndpoint ar;
-
-    @EndpointInject(uri = "mock://invalid-ack")
-    MockEndpoint invalid;
-
-    @Override
-    protected CamelContext createCamelContext() throws Exception {
-        DefaultCamelContext context = (DefaultCamelContext) super.createCamelContext();
-
-        context.setUseMDCLogging(true);
-        context.setName(this.getClass().getSimpleName());
-
-        return context;
-    }
-
-    @Override
-    protected RouteBuilder createRouteBuilder() throws Exception {
-        return new RouteBuilder() {
-            String routeId = "mllp-sender";
-
-            public void configure() {
-                onException(MllpApplicationRejectAcknowledgementException.class)
-                        .handled(true)
-                        .to(ar)
-                        .log(LoggingLevel.ERROR, routeId, "AR Acknowledgement");
-
-                onException(MllpApplicationErrorAcknowledgementException.class)
-                        .handled(true)
-                        .to(ae)
-                        .log(LoggingLevel.ERROR, routeId, "AE Acknowledgement");
-
-                onException(MllpInvalidAcknowledgementException.class)
-                        .handled(true)
-                        .to(invalid)
-                        .log(LoggingLevel.ERROR, routeId, "Invalid Acknowledgement");
-
-                onCompletion()
-                        .onFailureOnly()
-                        .to(failed)
-                        .log(LoggingLevel.DEBUG, routeId, "Exchange failed");
-
-                from(source.getDefaultEndpoint()).routeId(routeId)
-                        .log(LoggingLevel.INFO, routeId, "Sending Message")
-                        .toF("mllp://%s:%d?validatePayload=true", mllpServer.getListenHost(), mllpServer.getListenPort())
-                        .log(LoggingLevel.INFO, routeId, "Received Acknowledgement")
-                        .to(aa);
-            }
-        };
-    }
-
-    @Test
-    public void testApplicationAcceptAcknowledgement() throws Exception {
-        aa.expectedBodiesReceived(TEST_MESSAGE);
-        aa.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_TYPE, "AA");
-        aa.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT, EXPECTED_AA.getBytes());
-        aa.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING, EXPECTED_AA);
-
-        failed.expectedMessageCount(0);
-        failed.setAssertPeriod(1000);
-
-        ae.expectedMessageCount(0);
-        ar.expectedMessageCount(0);
-        invalid.expectedMessageCount(0);
-
-        source.sendBody(TEST_MESSAGE);
-
-        assertMockEndpointsSatisfied(15, TimeUnit.SECONDS);
-    }
-
-    @Test
-    public void testApplicationRejectAcknowledgement() throws Exception {
-        ar.expectedBodiesReceived(TEST_MESSAGE);
-        aa.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_TYPE, "AR");
-        ar.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT, EXPECTED_AR.getBytes());
-        ar.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING, EXPECTED_AR);
-
-        failed.expectedMessageCount(0);
-        failed.setAssertPeriod(1000);
-
-        aa.expectedMessageCount(0);
-        ae.expectedMessageCount(0);
-        invalid.expectedMessageCount(0);
-
-        mllpServer.setSendApplicationRejectAcknowledgementModulus(1);
-
-        source.sendBody(TEST_MESSAGE);
-
-        assertMockEndpointsSatisfied(15, TimeUnit.SECONDS);
-    }
-
-    @Test
-    public void testApplicationErrorAcknowledgement() throws Exception {
-        ae.expectedBodiesReceived(TEST_MESSAGE);
-        aa.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_TYPE, "AE");
-        ae.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT, EXPECTED_AE.getBytes());
-        ae.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING, EXPECTED_AE);
-
-        failed.expectedMessageCount(0);
-        failed.setAssertPeriod(1000);
-
-        aa.expectedMessageCount(0);
-        ar.expectedMessageCount(0);
-        invalid.expectedMessageCount(0);
-
-        mllpServer.setSendApplicationErrorAcknowledgementModulus(1);
-
-        source.sendBody(TEST_MESSAGE);
-
-        assertMockEndpointsSatisfied(15, TimeUnit.SECONDS);
-    }
-
-    @Test
-    public void testEmptyAcknowledgement() throws Exception {
-        invalid.expectedBodiesReceived(TEST_MESSAGE);
-        invalid.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT, "".getBytes());
-        invalid.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING, "");
-
-        failed.expectedMessageCount(0);
-        failed.setAssertPeriod(1000);
-
-        aa.expectedMessageCount(0);
-        ae.expectedMessageCount(0);
-        ar.expectedMessageCount(0);
-
-        mllpServer.setExcludeAcknowledgementModulus(1);
-
-        source.sendBody(TEST_MESSAGE);
-
-        assertMockEndpointsSatisfied(15, TimeUnit.SECONDS);
-    }
-
-    @Test
-    public void testInvalidAcknowledgement() throws Exception {
-        final String badAcknowledgement = "A VERY BAD ACKNOWLEDGEMENT";
-
-        invalid.expectedBodiesReceived(TEST_MESSAGE);
-        invalid.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING, badAcknowledgement.getBytes());
-        invalid.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING, badAcknowledgement);
-
-        failed.expectedMessageCount(0);
-        failed.setAssertPeriod(1000);
-
-        aa.expectedMessageCount(0);
-        ae.expectedMessageCount(0);
-        ar.expectedMessageCount(0);
-
-        mllpServer.setAcknowledgementString(badAcknowledgement);
-
-        source.sendBody(TEST_MESSAGE);
-
-        assertMockEndpointsSatisfied(15, TimeUnit.SECONDS);
-    }
-
-    @Test
-    public void testInvalidAcknowledgementContainingEmbeddedStartOfBlock() throws Exception {
-        final String badAcknowledgement = EXPECTED_AA.replaceFirst("RISTECH", "RISTECH" + START_OF_BLOCK);
-
-        invalid.expectedBodiesReceived(TEST_MESSAGE);
-        aa.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_TYPE, "AA");
-        invalid.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING, badAcknowledgement.getBytes());
-        invalid.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING, badAcknowledgement);
-
-        failed.expectedMessageCount(0);
-        failed.setAssertPeriod(1000);
-
-        aa.expectedMessageCount(0);
-        ae.expectedMessageCount(0);
-        ar.expectedMessageCount(0);
-
-        mllpServer.setAcknowledgementString(badAcknowledgement);
-
-        source.sendBody(TEST_MESSAGE);
-
-        assertMockEndpointsSatisfied(15, TimeUnit.SECONDS);
-    }
-
-    @Test
-    public void testInvalidAcknowledgementContainingEmbeddedEndOfBlock() throws Exception {
-        final String badAcknowledgement = EXPECTED_AA.replaceFirst("RISTECH", "RISTECH" + END_OF_BLOCK);
-
-        invalid.expectedBodiesReceived(TEST_MESSAGE);
-        aa.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_TYPE, "AA");
-        invalid.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING, badAcknowledgement.getBytes());
-        invalid.expectedHeaderReceived(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING, badAcknowledgement);
-
-        failed.expectedMessageCount(0);
-        failed.setAssertPeriod(1000);
-
-        aa.expectedMessageCount(0);
-        ae.expectedMessageCount(0);
-        ar.expectedMessageCount(0);
-
-        mllpServer.setAcknowledgementString(badAcknowledgement);
-
-        source.sendBody(TEST_MESSAGE);
-
-        assertMockEndpointsSatisfied(15, TimeUnit.SECONDS);
-    }
-}
diff --git a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpTcpClientProducerBlueprintTest.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpTcpClientProducerBlueprintTest.java
index 6d308bf..03a7d9a 100644
--- a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpTcpClientProducerBlueprintTest.java
+++ b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpTcpClientProducerBlueprintTest.java
@@ -14,6 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.camel.component.mllp;
 
 import java.util.Dictionary;
@@ -22,80 +23,71 @@ import java.util.Properties;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.camel.EndpointInject;
-import org.apache.camel.ProducerTemplate;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.impl.DefaultComponentResolver;
 import org.apache.camel.spi.ComponentResolver;
+
 import org.apache.camel.test.AvailablePortFinder;
 import org.apache.camel.test.blueprint.CamelBlueprintTestSupport;
+
 import org.apache.camel.test.junit.rule.mllp.MllpServerResource;
+
+import org.apache.camel.test.mllp.Hl7TestMessageGenerator;
 import org.apache.camel.util.KeyValueHolder;
 import org.junit.Rule;
 import org.junit.Test;
 
-import static org.apache.camel.test.mllp.Hl7MessageGenerator.generateMessage;
-
 public class MllpTcpClientProducerBlueprintTest extends CamelBlueprintTestSupport {
+    static final String SOURCE_URI = "direct-vm://source";
+    static final String MOCK_ACKNOWLEDGED_URI = "mock://acknowledged";
+    static final String MOCK_TIMEOUT_URI = "mock://timeoutError-ex";
+    static final String MOCK_AE_EX_URI = "mock://ae-ack";
+    static final String MOCK_AR_EX_URI = "mock://ar-ack";
+    static final String MOCK_FRAME_EX_URI = "mock://frameError-ex";
+
     @Rule
-    public MllpServerResource mllpServer = new MllpServerResource("localhost", AvailablePortFinder.getNextAvailable());
-
-    final String sourceUri = "direct://source";
-    final String mockAcknowledgedUri = "mock://acknowledged";
-    final String mockTimeoutUri = "mock://timeoutError-ex";
-    final String mockAeExUri = "mock://ae-ack";
-    final String mockArExUri = "mock://ar-ack";
-    final String mockFrameExUri = "mock://frameError-ex";
-
-    @EndpointInject(uri = sourceUri)
-    ProducerTemplate source;
-    @EndpointInject(uri = mockAcknowledgedUri)
+    public MllpServerResource mllpServer = new MllpServerResource("0.0.0.0", AvailablePortFinder.getNextAvailable());
+
+    @EndpointInject(uri = MOCK_ACKNOWLEDGED_URI)
     MockEndpoint acknowledged;
-    @EndpointInject(uri = mockTimeoutUri)
+
+    @EndpointInject(uri = MOCK_TIMEOUT_URI)
     MockEndpoint timeout;
-    @EndpointInject(uri = mockAeExUri)
+
+    @EndpointInject(uri = MOCK_AE_EX_URI)
     MockEndpoint ae;
-    @EndpointInject(uri = mockArExUri)
+
+    @EndpointInject(uri = MOCK_AR_EX_URI)
     MockEndpoint ar;
-    @EndpointInject(uri = mockFrameExUri)
+
+    @EndpointInject(uri = MOCK_FRAME_EX_URI)
     MockEndpoint frame;
 
     @Override
-    protected String getBlueprintDescriptor() {
-        return "OSGI-INF/blueprint/mllp-tcp-client-producer-test.xml";
+    protected void addServicesOnStartup(Map<String, KeyValueHolder<Object, Dictionary>> services) {
+        ComponentResolver testResolver = new DefaultComponentResolver();
+
+        services.put(ComponentResolver.class.getName(), asService(testResolver, "component", "mllp"));
     }
 
-    @Override
-    protected Properties useOverridePropertiesWithPropertiesComponent() {
-        Properties props = new Properties();
 
-        props.setProperty("sourceUri", sourceUri);
-        props.setProperty("acknowledgedUri", mockAcknowledgedUri);
-        props.setProperty("timeoutUri", mockTimeoutUri);
-        props.setProperty("frameErrorUri", mockFrameExUri);
-        props.setProperty("errorAcknowledgementUri", mockAeExUri);
-        props.setProperty("rejectAcknowledgementUri", mockArExUri);
+    @Override
+    protected String setConfigAdminInitialConfiguration(Properties props) {
+        props.setProperty("sourceUri", SOURCE_URI);
+        props.setProperty("acknowledgedUri", MOCK_ACKNOWLEDGED_URI);
+        props.setProperty("timeoutUri", MOCK_TIMEOUT_URI);
+        props.setProperty("errorAcknowledgementUri", MOCK_AE_EX_URI);
+        props.setProperty("rejectAcknowledgementUri", MOCK_AR_EX_URI);
 
         props.setProperty("mllp.port", Integer.toString(mllpServer.getListenPort()));
 
-        return props;
+        return "MllpTcpClientProducer";
     }
 
-    /*
-        This doesn't seem to work
-        @Override
-        protected String useOverridePropertiesWithConfigAdmin(Dictionary props) throws Exception {
-
-            props.put("mllp.port", mllpServer.getListenPort() );
-
-            return "MllpTcpClientProducer";
-        }
-    */
 
     @Override
-    protected void addServicesOnStartup(Map<String, KeyValueHolder<Object, Dictionary>> services) {
-        ComponentResolver testResolver = new DefaultComponentResolver();
-
-        services.put(ComponentResolver.class.getName(), asService(testResolver, "component", "mllp"));
+    protected String getBlueprintDescriptor() {
+        return "OSGI-INF/blueprint/mllp-tcp-client-producer-test.xml";
     }
 
     @Test()
@@ -107,18 +99,19 @@ public class MllpTcpClientProducerBlueprintTest extends CamelBlueprintTestSuppor
         ae.expectedMessageCount(0);
         ar.expectedMessageCount(0);
 
+        startCamelContext();
         // Uncomment one of these lines to see the NACKs handled
         // mllpServer.setSendApplicationRejectAcknowledgementModulus(10);
         // mllpServer.setSendApplicationErrorAcknowledgementModulus(10);
 
         for (int i = 0; i < messageCount; ++i) {
             log.debug("Triggering message {}", i);
-            Object response = source.requestBodyAndHeader(generateMessage(i), "CamelMllpMessageControlId", String.format("%05d", i));
+            // Thread.sleep(5000);
+            Object response = template.requestBodyAndHeader(SOURCE_URI, Hl7TestMessageGenerator.generateMessage(i), "CamelMllpMessageControlId", String.format("%05d", i));
             log.debug("response {}\n{}", i, response);
         }
 
         assertMockEndpointsSatisfied(15, TimeUnit.SECONDS);
     }
 
-
 }
diff --git a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpTcpClientProducerConnectionErrorTest.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpTcpClientProducerConnectionErrorTest.java
index 897b74b..c348438 100644
--- a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpTcpClientProducerConnectionErrorTest.java
+++ b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpTcpClientProducerConnectionErrorTest.java
@@ -14,8 +14,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.camel.component.mllp;
 
+import java.net.ConnectException;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.camel.CamelContext;
@@ -26,15 +28,16 @@ import org.apache.camel.builder.NotifyBuilder;
 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.junit.rule.mllp.MllpServerResource;
 import org.apache.camel.test.junit4.CamelTestSupport;
 
+import org.apache.camel.test.mllp.Hl7TestMessageGenerator;
+
 import org.junit.Rule;
 import org.junit.Test;
 
-import static org.apache.camel.test.mllp.Hl7MessageGenerator.generateMessage;
-
 public class MllpTcpClientProducerConnectionErrorTest extends CamelTestSupport {
     @Rule
     public MllpServerResource mllpServer = new MllpServerResource("localhost", AvailablePortFinder.getNextAvailable());
@@ -48,6 +51,9 @@ public class MllpTcpClientProducerConnectionErrorTest extends CamelTestSupport {
     @EndpointInject(uri = "mock://write-ex")
     MockEndpoint writeEx;
 
+    @EndpointInject(uri = "mock://connect-ex")
+    MockEndpoint connectEx;
+
     @EndpointInject(uri = "mock://receive-ex")
     MockEndpoint receiveEx;
 
@@ -68,62 +74,84 @@ public class MllpTcpClientProducerConnectionErrorTest extends CamelTestSupport {
             String routeId = "mllp-sender";
 
             public void configure() {
+                onException(ConnectException.class)
+                    .handled(true)
+                    .to(connectEx)
+                    .log(LoggingLevel.ERROR, routeId, "Connect Error")
+                    .stop();
+
                 onException(MllpWriteException.class)
-                        .handled(true)
-                        .to(writeEx)
-                        .log(LoggingLevel.ERROR, routeId, "Write Error")
-                        .stop();
+                    .handled(true)
+                    .to(writeEx)
+                    .log(LoggingLevel.ERROR, routeId, "Write Error")
+                    .stop();
 
-                onException(MllpReceiveAcknowledgementException.class)
-                        .handled(true)
-                        .to(receiveEx)
-                        .log(LoggingLevel.ERROR, routeId, "Receive Error")
-                        .stop();
+                onException(MllpAcknowledgementReceiveException.class)
+                    .handled(true)
+                    .to(receiveEx)
+                    .log(LoggingLevel.ERROR, routeId, "Receive Error")
+                    .stop();
 
                 from(source.getDefaultEndpoint()).routeId(routeId)
-                        .log(LoggingLevel.INFO, routeId, "Sending Message")
-                        .toF("mllp://%s:%d", mllpServer.getListenHost(), mllpServer.getListenPort())
-                        .log(LoggingLevel.INFO, routeId, "Received Acknowledgement")
-                        .to(complete);
+                    .log(LoggingLevel.INFO, routeId, "Sending Message")
+                    .toF("mllp://%s:%d", mllpServer.getListenHost(), mllpServer.getListenPort())
+                    .log(LoggingLevel.INFO, routeId, "Received Acknowledgement")
+                    .to(complete);
             }
         };
     }
 
+    /**
+     * The component should reconnect, so the route shouldn't see any errors.
+     *
+     * @throws Exception
+     */
     @Test
     public void testConnectionClosedBeforeSendingHL7Message() throws Exception {
-        complete.expectedMessageCount(1);
+        complete.expectedMessageCount(2);
+        connectEx.expectedMessageCount(0);
         writeEx.expectedMessageCount(0);
-        receiveEx.expectedMessageCount(1);
+        receiveEx.expectedMessageCount(0);
 
-        NotifyBuilder done = new NotifyBuilder(context).whenCompleted(2).create();
+        NotifyBuilder oneDone = new NotifyBuilder(context).whenCompleted(1).create();
+        NotifyBuilder twoDone = new NotifyBuilder(context).whenCompleted(2).create();
 
         // Need to send one message to get the connection established
-        source.sendBody(generateMessage());
+        source.sendBody(Hl7TestMessageGenerator.generateMessage());
+        assertTrue("Should have completed an exchange", oneDone.matches(5, TimeUnit.SECONDS));
 
         mllpServer.closeClientConnections();
-        source.sendBody(generateMessage());
 
-        assertTrue("Should have completed an exchange", done.matches(5, TimeUnit.SECONDS));
+        source.sendBody(Hl7TestMessageGenerator.generateMessage());
+
+        assertTrue("Should have completed two exchanges", twoDone.matches(5, TimeUnit.SECONDS));
 
         assertMockEndpointsSatisfied(5, TimeUnit.SECONDS);
     }
 
+    /**
+     * The component should reconnect, so the route shouldn't see any errors.
+     *
+     * @throws Exception
+     */
     @Test()
     public void testConnectionResetBeforeSendingHL7Message() throws Exception {
-        complete.expectedMessageCount(1);
-        writeEx.expectedMessageCount(1);
+        complete.expectedMessageCount(2);
+        connectEx.expectedMessageCount(0);
+        writeEx.expectedMessageCount(0);
         receiveEx.expectedMessageCount(0);
 
-        NotifyBuilder done = new NotifyBuilder(context).whenCompleted(2).create();
+        NotifyBuilder oneDone = new NotifyBuilder(context).whenCompleted(1).create();
+        NotifyBuilder twoDone = new NotifyBuilder(context).whenCompleted(2).create();
 
         // Need to send one message to get the connection established
-        source.sendBody(generateMessage());
+        source.sendBody(Hl7TestMessageGenerator.generateMessage());
+        assertTrue("Should have completed an exchange", oneDone.matches(5, TimeUnit.SECONDS));
 
         mllpServer.resetClientConnections();
 
-        source.sendBody(generateMessage());
-
-        assertTrue("Should have completed an exchange", done.matches(5, TimeUnit.SECONDS));
+        source.sendBody(Hl7TestMessageGenerator.generateMessage());
+        assertTrue("Should have completed two exchanges", twoDone.matches(5, TimeUnit.SECONDS));
 
         assertMockEndpointsSatisfied(5, TimeUnit.SECONDS);
     }
@@ -131,6 +159,7 @@ public class MllpTcpClientProducerConnectionErrorTest extends CamelTestSupport {
     @Test()
     public void testConnectionClosedBeforeReadingAcknowledgement() throws Exception {
         complete.expectedMessageCount(0);
+        connectEx.expectedMessageCount(0);
         writeEx.expectedMessageCount(0);
         receiveEx.expectedMessageCount(1);
 
@@ -138,7 +167,7 @@ public class MllpTcpClientProducerConnectionErrorTest extends CamelTestSupport {
 
         NotifyBuilder done = new NotifyBuilder(context).whenCompleted(1).create();
 
-        source.sendBody(generateMessage());
+        source.sendBody(Hl7TestMessageGenerator.generateMessage());
 
         assertTrue("Should have completed an exchange", done.matches(5, TimeUnit.SECONDS));
 
@@ -148,6 +177,7 @@ public class MllpTcpClientProducerConnectionErrorTest extends CamelTestSupport {
     @Test()
     public void testConnectionResetBeforeReadingAcknowledgement() throws Exception {
         complete.expectedMessageCount(0);
+        connectEx.expectedMessageCount(0);
         writeEx.expectedMessageCount(0);
         receiveEx.expectedMessageCount(1);
 
@@ -155,11 +185,80 @@ public class MllpTcpClientProducerConnectionErrorTest extends CamelTestSupport {
 
         NotifyBuilder done = new NotifyBuilder(context).whenCompleted(1).create();
 
-        source.sendBody(generateMessage());
+        source.sendBody(Hl7TestMessageGenerator.generateMessage());
 
         assertTrue("Should have completed an exchange", done.matches(5, TimeUnit.SECONDS));
 
         assertMockEndpointsSatisfied(5, TimeUnit.SECONDS);
     }
 
+
+    @Test()
+    public void testServerShutdownBeforeSendingHL7Message() throws Exception {
+        complete.expectedMessageCount(1);
+        connectEx.expectedMessageCount(0);
+
+        NotifyBuilder done = new NotifyBuilder(context).whenCompleted(2).create();
+
+        // Need to send one message to get the connection established
+        source.sendBody(Hl7TestMessageGenerator.generateMessage());
+
+        mllpServer.shutdown();
+
+        source.sendBody(Hl7TestMessageGenerator.generateMessage());
+
+        assertTrue("Should have completed an exchange", done.matches(5, TimeUnit.SECONDS));
+
+        assertMockEndpointsSatisfied(5, TimeUnit.SECONDS);
+
+        // Depending on the timing, either a write or a receive exception will be thrown
+        assertEquals("Either a write or a receive exception should have been be thrown", 1, writeEx.getExchanges().size() + receiveEx.getExchanges().size());
+    }
+
+    @Test()
+    public void testConnectionCloseAndServerShutdownBeforeSendingHL7Message() throws Exception {
+        complete.expectedMessageCount(1);
+        connectEx.expectedMessageCount(0);
+
+        NotifyBuilder done = new NotifyBuilder(context).whenCompleted(2).create();
+
+        // Need to send one message to get the connection established
+        source.sendBody(Hl7TestMessageGenerator.generateMessage());
+
+        mllpServer.closeClientConnections();
+        mllpServer.shutdown();
+
+        source.sendBody(Hl7TestMessageGenerator.generateMessage());
+
+        assertTrue("Should have completed an exchange", done.matches(5, TimeUnit.SECONDS));
+
+        assertMockEndpointsSatisfied(5, TimeUnit.SECONDS);
+
+        // Depending on the timing, either a write or a receive exception will be thrown
+        assertEquals("Either a write or a receive exception should have been be thrown", 1, writeEx.getExchanges().size() + receiveEx.getExchanges().size());
+    }
+
+    @Test()
+    public void testConnectionResetAndServerShutdownBeforeSendingHL7Message() throws Exception {
+        complete.expectedMessageCount(1);
+        connectEx.expectedMessageCount(1);
+        writeEx.expectedMessageCount(0);
+        receiveEx.expectedMessageCount(0);
+
+        NotifyBuilder done = new NotifyBuilder(context).whenCompleted(2).create();
+
+        // Need to send one message to get the connection established
+        source.sendBody(Hl7TestMessageGenerator.generateMessage());
+
+        mllpServer.resetClientConnections();
+        mllpServer.shutdown();
+
+        source.sendBody(Hl7TestMessageGenerator.generateMessage());
+
+        assertTrue("Should have completed an exchange", done.matches(5, TimeUnit.SECONDS));
+
+        assertMockEndpointsSatisfied(5, TimeUnit.SECONDS);
+    }
+
+
 }
diff --git a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpTcpClientProducerConnectionErrorTest.java b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpTcpClientProducerIdleConnectionTimeoutTest.java
similarity index 52%
copy from components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpTcpClientProducerConnectionErrorTest.java
copy to components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpTcpClientProducerIdleConnectionTimeoutTest.java
index 897b74b..9121136 100644
--- a/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpTcpClientProducerConnectionErrorTest.java
+++ b/components/camel-mllp/src/test/java/org/apache/camel/component/mllp/MllpTcpClientProducerIdleConnectionTimeoutTest.java
@@ -14,8 +14,10 @@
  * 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;
@@ -26,16 +28,22 @@ import org.apache.camel.builder.NotifyBuilder;
 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.junit.rule.mllp.MllpJUnitResourceException;
 import org.apache.camel.test.junit.rule.mllp.MllpServerResource;
+
 import org.apache.camel.test.junit4.CamelTestSupport;
 
+import org.apache.camel.test.mllp.Hl7TestMessageGenerator;
+
 import org.junit.Rule;
 import org.junit.Test;
 
-import static org.apache.camel.test.mllp.Hl7MessageGenerator.generateMessage;
+public class MllpTcpClientProducerIdleConnectionTimeoutTest extends CamelTestSupport {
+    static final int IDLE_TIMEOUT = 10000;
 
-public class MllpTcpClientProducerConnectionErrorTest extends CamelTestSupport {
     @Rule
     public MllpServerResource mllpServer = new MllpServerResource("localhost", AvailablePortFinder.getNextAvailable());
 
@@ -69,97 +77,78 @@ public class MllpTcpClientProducerConnectionErrorTest extends CamelTestSupport {
 
             public void configure() {
                 onException(MllpWriteException.class)
-                        .handled(true)
-                        .to(writeEx)
-                        .log(LoggingLevel.ERROR, routeId, "Write Error")
-                        .stop();
+                    .handled(true)
... 10425 lines suppressed ...

-- 
To stop receiving notification emails like this one, please contact
['"commits@camel.apache.org" <co...@camel.apache.org>'].