camel-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From davscl...@apache.org
Subject [1/2] camel git commit: CAMEL-8079: Provide possibility to delegate charset evaluation of a HL7 message to HL7DataFormat
Date Wed, 03 Dec 2014 19:11:30 GMT
Repository: camel
Updated Branches:
  refs/heads/camel-2.14.x f6dd81278 -> d76f78c59
  refs/heads/master 4a5666d46 -> 4ba970668


CAMEL-8079: Provide possibility to delegate charset evaluation of a HL7 message to HL7DataFormat


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/4ba97066
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/4ba97066
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/4ba97066

Branch: refs/heads/master
Commit: 4ba97066833b412877597e32cc32802dd2b18760
Parents: 4a5666d
Author: Claus Ibsen <davsclaus@apache.org>
Authored: Wed Dec 3 20:10:56 2014 +0100
Committer: Claus Ibsen <davsclaus@apache.org>
Committed: Wed Dec 3 20:10:56 2014 +0100

----------------------------------------------------------------------
 .../org/apache/camel/component/hl7/HL7.java     |  29 ++-
 .../apache/camel/component/hl7/HL7Charset.java  | 123 +++++++++++
 .../camel/component/hl7/HL7Constants.java       |   1 +
 .../camel/component/hl7/HL7DataFormat.java      |  47 ++--
 .../camel/component/hl7/HL7MLLPCodec.java       |   7 +
 .../camel/component/hl7/HL7MLLPConfig.java      |  10 +
 .../camel/component/hl7/HL7MLLPDecoder.java     |  28 ++-
 .../camel/component/hl7/AckExpressionTest.java  |   7 +-
 .../component/hl7/HL7ByteArrayRouteTest.java    | 219 +++++++++++++++++++
 .../camel/component/hl7/HL7DataFormatTest.java  |  93 +++++++-
 10 files changed, 526 insertions(+), 38 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/4ba97066/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7.java
----------------------------------------------------------------------
diff --git a/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7.java b/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7.java
index c1a6ae2..d9e8fdf 100644
--- a/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7.java
+++ b/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7.java
@@ -37,16 +37,23 @@ public final class HL7 {
         return new ValueBuilder(new TerserExpression(expression));
     }
 
-    public static Expression ack() {
-        return new AckExpression();
+    public static ValueBuilder ack() {
+        return new ValueBuilder(new AckExpression());
     }
 
-    public static Expression ack(AckCode code) {
-        return new AckExpression(code);
+    /**
+     * @deprecated Use {@link #ack(ca.uhn.hl7v2.AcknowledgmentCode)}
+     */
+    public static ValueBuilder ack(AckCode code) {
+        return new ValueBuilder(new AckExpression(code));
+    }
+
+    public static ValueBuilder ack(AcknowledgmentCode code) {
+        return new ValueBuilder(new AckExpression(code));
     }
 
-    public static Expression convertLFToCR() {
-        return new ExpressionAdapter() {
+    public static ValueBuilder convertLFToCR() {
+        return new ValueBuilder(new ExpressionAdapter() {
 
             @Override
             public Object evaluate(Exchange exchange) {
@@ -54,14 +61,14 @@ public final class HL7 {
                 return s != null ? s.replace('\n', '\r') : null;
             }
 
-        };
+        });
     }
 
     /**
      * @deprecated Use {@link #ack(ca.uhn.hl7v2.AcknowledgmentCode, String, ErrorCode)}
      */
     @Deprecated
-    public static Expression ack(AckCode code, String errorMessage, int errorCode) {
+    public static ValueBuilder ack(AckCode code, String errorMessage, int errorCode) {
         return ack(code.toAcknowledgmentCode(), errorMessage, ErrorCode.errorCodeFor(errorCode));
     }
 
@@ -69,12 +76,12 @@ public final class HL7 {
      * @deprecated Use {@link #ack(ca.uhn.hl7v2.AcknowledgmentCode, String, ErrorCode)}
      */
     @Deprecated
-    public static Expression ack(AckCode code, String errorMessage, ErrorCode errorCode)
{
+    public static ValueBuilder ack(AckCode code, String errorMessage, ErrorCode errorCode)
{
         return ack(code.toAcknowledgmentCode(), errorMessage, errorCode);
     }
 
-    public static Expression ack(AcknowledgmentCode code, String errorMessage, ErrorCode
errorCode) {
-        return new AckExpression(code, errorMessage, errorCode);
+    public static ValueBuilder ack(AcknowledgmentCode code, String errorMessage, ErrorCode
errorCode) {
+        return new ValueBuilder(new AckExpression(code, errorMessage, errorCode));
     }
 
     public static Predicate messageConforms() {

http://git-wip-us.apache.org/repos/asf/camel/blob/4ba97066/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7Charset.java
----------------------------------------------------------------------
diff --git a/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7Charset.java
b/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7Charset.java
new file mode 100644
index 0000000..0e419ed
--- /dev/null
+++ b/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7Charset.java
@@ -0,0 +1,123 @@
+/**
+ * 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.hl7;
+
+import java.io.UnsupportedEncodingException;
+
+import ca.uhn.hl7v2.HL7Exception;
+import ca.uhn.hl7v2.model.Message;
+import ca.uhn.hl7v2.model.Segment;
+import ca.uhn.hl7v2.preparser.PreParser;
+import org.apache.camel.Exchange;
+import org.apache.camel.util.IOHelper;
+
+/**
+ * This enumerates the defined charsets for HL7 as defined in Table 0211,
+ * mapping them to the Java charset names and back
+ */
+public enum HL7Charset {
+
+    ISO_8859_1("8859/1", "ISO-8859-1"),
+    ISO_8859_2("8859/2", "ISO-8859-2"),
+    ISO_8859_3("8859/3", "ISO-8859-3"),
+    ISO_8859_4("8859/4", "ISO-8859-4"),
+    ISO_8859_5("8859/5", "ISO-8859-5"),
+    ISO_8859_6("8859/1", "ISO-8859-6"),
+    ISO_8859_7("8859/1", "ISO-8859-7"),
+    ISO_8859_8("8859/1", "ISO-8859-8"),
+    ISO_8859_9("8859/1", "ISO-8859-9"),
+    ASCII("ASCII", "US-ASCII"),
+    BIG_5("BIG-5", "Big5"),
+    CNS("CNS 11643-1992", "ISO-2022-CN"),
+    GB_1830_2000("GB 18030-2000", ""),
+    ISO_IR14("ISO IR14", "ISO-2022-JP"),
+    ISO_IR159("ISO IR159", "EUC-JP"),
+    ISO_IR87("ISO IR87", "EUC-JP"),
+    KS_X_1001("KS X 1001", "EUC-KR"),
+    UNICODE("UNICODE", "UTF-8"),
+    UTF_16("UNICODE UTF-16", "UTF-16"),
+    UTF_32("UNICODE UTF-32", "UTF-32"),
+    UTF_8("UNICODE UTF-8", "UTF-8");
+
+    private String hl7CharsetName;
+    private String javaCharsetName;
+
+    HL7Charset(String hl7CharsetName, String javaCharsetName) {
+        this.hl7CharsetName = hl7CharsetName;
+        this.javaCharsetName = javaCharsetName;
+    }
+
+    public String getHL7CharsetName() {
+        return hl7CharsetName;
+    }
+
+    public String getJavaCharsetName() {
+        return javaCharsetName;
+    }
+
+    /**
+     * Returns the HL7Charset that matches the parameter
+     *
+     * @param s charset string
+     * @return HL7Charset enum
+     */
+    public static HL7Charset getHL7Charset(String s) {
+        if (s != null && s.length() > 0) {
+            for (HL7Charset charset : HL7Charset.values()) {
+                if (charset.hl7CharsetName.equals(s) || charset.javaCharsetName.equals(s))
{
+                    return charset;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the charset to be used for marshalling HL7 messages. If MSH-18 is empty,
+     * the charset configured in Camel's charset properties/headers is returned.
+     *
+     * @param message HL7 message
+     * @param exchange Exchange
+     * @return Java charset name
+     */
+    public static String getCharsetName(Message message, Exchange exchange) throws HL7Exception
{
+        String defaultCharsetName = IOHelper.getCharsetName(exchange);
+        String msh18 = ((Segment)message.get("MSH")).getField(18, 0).toString();
+        return getCharsetName(msh18, defaultCharsetName);
+    }
+
+    /**
+     * Returns the charset to be used for unmarshalling HL7 messages. If MSH-18 is empty,
+     * the temporary charset name is returned.
+     *
+     * @param bytes HL7 message as byte array
+     * @param guessedCharsetName the temporary charset guessed to be able to read MSH-18
+     * @return Java charset name
+     *
+     * @see org.apache.camel.component.hl7.HL7DataFormat#guessCharsetName(byte[], org.apache.camel.Exchange)
+     */
+    public static String getCharsetName(byte[] bytes, String guessedCharsetName) throws UnsupportedEncodingException,
HL7Exception {
+        String tmp = new String(bytes, guessedCharsetName);
+        String msh18 = PreParser.getFields(tmp, "MSH-18")[0];
+        return getCharsetName(msh18, guessedCharsetName);
+    }
+
+    private static String getCharsetName(String msh18, String defaultCharsetName) {
+        HL7Charset charset = HL7Charset.getHL7Charset(msh18);
+        return charset != null ? charset.getJavaCharsetName() : defaultCharsetName;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/4ba97066/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7Constants.java
----------------------------------------------------------------------
diff --git a/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7Constants.java
b/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7Constants.java
index 50b103d..05ca784 100644
--- a/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7Constants.java
+++ b/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7Constants.java
@@ -33,6 +33,7 @@ public final class HL7Constants {
     public static final String HL7_PROCESSING_ID = "CamelHL7ProcessingId";
     public static final String HL7_VERSION_ID = "CamelHL7VersionId";
     public static final String HL7_CONTEXT = "CamelHL7Context";
+    public static final String HL7_CHARSET = "CamelHL7Charset";
 
     private HL7Constants() {
     }

http://git-wip-us.apache.org/repos/asf/camel/blob/4ba97066/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7DataFormat.java
----------------------------------------------------------------------
diff --git a/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7DataFormat.java
b/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7DataFormat.java
index 7acbee8..6f2abf8 100644
--- a/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7DataFormat.java
+++ b/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7DataFormat.java
@@ -33,18 +33,7 @@ import org.apache.camel.support.ServiceSupport;
 import org.apache.camel.util.ExchangeHelper;
 import org.apache.camel.util.IOHelper;
 
-import static org.apache.camel.component.hl7.HL7Constants.HL7_CONTEXT;
-import static org.apache.camel.component.hl7.HL7Constants.HL7_MESSAGE_CONTROL;
-import static org.apache.camel.component.hl7.HL7Constants.HL7_MESSAGE_TYPE;
-import static org.apache.camel.component.hl7.HL7Constants.HL7_PROCESSING_ID;
-import static org.apache.camel.component.hl7.HL7Constants.HL7_RECEIVING_APPLICATION;
-import static org.apache.camel.component.hl7.HL7Constants.HL7_RECEIVING_FACILITY;
-import static org.apache.camel.component.hl7.HL7Constants.HL7_SECURITY;
-import static org.apache.camel.component.hl7.HL7Constants.HL7_SENDING_APPLICATION;
-import static org.apache.camel.component.hl7.HL7Constants.HL7_SENDING_FACILITY;
-import static org.apache.camel.component.hl7.HL7Constants.HL7_TIMESTAMP;
-import static org.apache.camel.component.hl7.HL7Constants.HL7_TRIGGER_EVENT;
-import static org.apache.camel.component.hl7.HL7Constants.HL7_VERSION_ID;
+import static org.apache.camel.component.hl7.HL7Constants.*;
 
 /**
  * HL7 DataFormat (supports v2.x of the HL7 protocol).
@@ -74,6 +63,7 @@ import static org.apache.camel.component.hl7.HL7Constants.HL7_VERSION_ID;
  *   <li>CamelHL7MessageControl = MSH-10</li>
  *   <li>CamelHL7ProcessingId = MSH-11</li>
  *   <li>CamelHL7VersionId = MSH-12</li>
+ *   <li>CamelHL7Charset = MSH-18</li>
  * </ul>
  * All headers are String types.
  * <p/>
@@ -100,18 +90,21 @@ public class HL7DataFormat extends ServiceSupport implements DataFormat
{
         HEADER_MAP.put(HL7_MESSAGE_CONTROL, "MSH-10");
         HEADER_MAP.put(HL7_PROCESSING_ID, "MSH-11");
         HEADER_MAP.put(HL7_VERSION_ID, "MSH-12");
+        HEADER_MAP.put(HL7_CHARSET, "MSH-18");
     }
 
     public void marshal(Exchange exchange, Object body, OutputStream outputStream) throws
Exception {
         Message message = ExchangeHelper.convertToMandatoryType(exchange, Message.class,
body);
+        String charsetName = HL7Charset.getCharsetName(message, exchange);
         String encoded = HL7Converter.encode(message, parser);
-        String charsetName = IOHelper.getCharsetName(exchange);
         outputStream.write(encoded.getBytes(charsetName));
     }
 
     public Object unmarshal(Exchange exchange, InputStream inputStream) throws Exception
{
-        String body = ExchangeHelper.convertToMandatoryType(exchange, String.class, inputStream);
-        Message message = HL7Converter.parse(body, parser);
+        byte[] body = ExchangeHelper.convertToMandatoryType(exchange, byte[].class, inputStream);
+        String charsetName = HL7Charset.getCharsetName(body, guessCharsetName(body, exchange));
+        String bodyAsString = new String(body, charsetName);
+        Message message = HL7Converter.parse(bodyAsString, parser);
 
         // add MSH fields as message out headers
         Terser terser = new Terser(message);
@@ -119,6 +112,7 @@ public class HL7DataFormat extends ServiceSupport implements DataFormat
{
             exchange.getOut().setHeader(entry.getKey(), terser.get(entry.getValue()));
         }
         exchange.getOut().setHeader(HL7_CONTEXT, hapiContext);
+        exchange.getOut().setHeader(Exchange.CHARSET_NAME, charsetName);
         return message;
     }
 
@@ -147,6 +141,7 @@ public class HL7DataFormat extends ServiceSupport implements DataFormat
{
     }
 
 
+
     @Override
     protected void doStart() throws Exception {
         if (hapiContext == null) {
@@ -164,5 +159,27 @@ public class HL7DataFormat extends ServiceSupport implements DataFormat
{
     protected void doStop() throws Exception {
         // noop
     }
+
+
+    /**
+     * In HL7 the charset of the message can be set in MSH-18,
+     * but you need to decode the input stream in order to be able to read MSH-18.
+     * This works well for differentiating e.g. between ASCII, UTF-8 and ISI-8859 charsets,
+     * but not for multi-byte charsets like UTF-16, Big5 etc.
+     *
+     * This method is called to "guess" the initial encoding, and subclasses can overwrite
it
+     * using 3rd party libraries like ICU4J that provide a CharsetDetector.
+     *
+     * The implementation in this class just assumes the charset defined in the exchange
property or header by
+     * calling {@link org.apache.camel.util.IOHelper#getCharsetName(org.apache.camel.Exchange)}.
+     *
+     * @param b byte array
+     * @param exchange
+     * @return charset name
+     */
+    protected String guessCharsetName(byte[] b, Exchange exchange) {
+        return IOHelper.getCharsetName(exchange);
+    }
+
 }
 

http://git-wip-us.apache.org/repos/asf/camel/blob/4ba97066/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7MLLPCodec.java
----------------------------------------------------------------------
diff --git a/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7MLLPCodec.java
b/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7MLLPCodec.java
index 5fff6b6..8472d03 100644
--- a/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7MLLPCodec.java
+++ b/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7MLLPCodec.java
@@ -109,4 +109,11 @@ public class HL7MLLPCodec implements ProtocolCodecFactory {
         config.setValidate(validate);
     }
 
+    public boolean isProduceString() {
+        return config.isProduceString();
+    }
+
+    public void setProduceString(boolean apply) {
+        config.setProduceString(apply);
+    }
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/4ba97066/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7MLLPConfig.java
----------------------------------------------------------------------
diff --git a/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7MLLPConfig.java
b/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7MLLPConfig.java
index 61e4e45..4ec78d2 100644
--- a/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7MLLPConfig.java
+++ b/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7MLLPConfig.java
@@ -39,6 +39,8 @@ class HL7MLLPConfig {
 
     private Parser parser = hapiContext.getGenericParser();
 
+    private boolean produceString = true;
+
     public Charset getCharset() {
         return charset;
     }
@@ -103,4 +105,12 @@ class HL7MLLPConfig {
     public void setValidate(boolean validate) {
         parser.getParserConfiguration().setValidating(validate);
     }
+
+    public boolean isProduceString() {
+        return produceString;
+    }
+
+    public void setProduceString(boolean produceString) {
+        this.produceString = produceString;
+    }
 }

http://git-wip-us.apache.org/repos/asf/camel/blob/4ba97066/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7MLLPDecoder.java
----------------------------------------------------------------------
diff --git a/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7MLLPDecoder.java
b/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7MLLPDecoder.java
index 3c616e1..4a54d47 100644
--- a/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7MLLPDecoder.java
+++ b/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7MLLPDecoder.java
@@ -72,7 +72,9 @@ class HL7MLLPDecoder extends CumulativeProtocolDecoder {
                     // The bytes between in.position() and in.limit()
                     // now contain a full MLLP message including the
                     // start and end bytes.
-                    out.write(parseMessage(in.slice(), charsetDecoder(session)));
+                    out.write(config.isProduceString()
+                            ? parseMessageToString(in.slice(), charsetDecoder(session))
+                            : parseMessageToByteArray(in.slice()));
                 } catch (CharacterCodingException cce) {
                     throw new IllegalArgumentException("Exception while finalizing the message",
cce);
                 } finally {
@@ -99,9 +101,27 @@ class HL7MLLPDecoder extends CumulativeProtocolDecoder {
 
     // Make a defensive byte copy (the buffer will be reused)
     // and omit the start and the two end bytes of the MLLP message
-    // TODO: I wonder if it would make sense to return the plain byte array and let some
subsequent
-    // processor do the conversion
-    private Object parseMessage(IoBuffer slice, CharsetDecoder decoder) throws CharacterCodingException
{
+    // returning a byte array
+    private Object parseMessageToByteArray(IoBuffer slice) throws CharacterCodingException
{
+        byte[] dst = new byte[slice.limit() - 3];
+        slice.skip(1); // skip start byte
+        slice.get(dst, 0, dst.length);
+
+        // Only do this if conversion is enabled
+        if (config.isConvertLFtoCR()) {
+            for (int i = 0; i < dst.length; i++) {
+                if (dst[i] == (byte)'\n') {
+                    dst[i] = (byte)'\r';
+                }
+            }
+        }
+        return dst;
+    }
+
+    // Make a defensive byte copy (the buffer will be reused)
+    // and omit the start and the two end bytes of the MLLP message
+    // returning a String
+    private Object parseMessageToString(IoBuffer slice, CharsetDecoder decoder) throws CharacterCodingException
{
         slice.skip(1); // skip start byte
         String message = slice.getString(slice.limit() - 3, decoder);
 

http://git-wip-us.apache.org/repos/asf/camel/blob/4ba97066/components/camel-hl7/src/test/java/org/apache/camel/component/hl7/AckExpressionTest.java
----------------------------------------------------------------------
diff --git a/components/camel-hl7/src/test/java/org/apache/camel/component/hl7/AckExpressionTest.java
b/components/camel-hl7/src/test/java/org/apache/camel/component/hl7/AckExpressionTest.java
index e9be437..fa60d6d 100644
--- a/components/camel-hl7/src/test/java/org/apache/camel/component/hl7/AckExpressionTest.java
+++ b/components/camel-hl7/src/test/java/org/apache/camel/component/hl7/AckExpressionTest.java
@@ -15,6 +15,7 @@
  * limitations under the License.
  */
 package org.apache.camel.component.hl7;
+import ca.uhn.hl7v2.AcknowledgmentCode;
 import ca.uhn.hl7v2.ErrorCode;
 import ca.uhn.hl7v2.HL7Exception;
 import ca.uhn.hl7v2.model.v24.message.ACK;
@@ -91,13 +92,13 @@ public class AckExpressionTest extends CamelTestSupport {
         return new RouteBuilder() {
             public void configure() throws Exception {
                 from("direct:test1").transform(ack());
-                from("direct:test2").transform(ack(AckCode.CA));
+                from("direct:test2").transform(ack(AcknowledgmentCode.CA));
                 from("direct:test3").onException(HL7Exception.class).handled(true).transform(ack()).end()
                     .transform(terser("/.BLORG"));
                 from("direct:test4").onException(HL7Exception.class).handled(true)
-                    .transform(ack(AckCode.AR, "Problem!", ErrorCode.APPLICATION_INTERNAL_ERROR)).end()
+                    .transform(ack(AcknowledgmentCode.AR, "Problem!", ErrorCode.APPLICATION_INTERNAL_ERROR)).end()
                     .transform(terser("/.BLORG"));
-                from("direct:test5").transform(ack(AckCode.AR, "Problem!", ErrorCode.DATA_TYPE_ERROR));
+                from("direct:test5").transform(ack(AcknowledgmentCode.AR, "Problem!", ErrorCode.DATA_TYPE_ERROR));
             }
         };
     }

http://git-wip-us.apache.org/repos/asf/camel/blob/4ba97066/components/camel-hl7/src/test/java/org/apache/camel/component/hl7/HL7ByteArrayRouteTest.java
----------------------------------------------------------------------
diff --git a/components/camel-hl7/src/test/java/org/apache/camel/component/hl7/HL7ByteArrayRouteTest.java
b/components/camel-hl7/src/test/java/org/apache/camel/component/hl7/HL7ByteArrayRouteTest.java
new file mode 100644
index 0000000..38da5df
--- /dev/null
+++ b/components/camel-hl7/src/test/java/org/apache/camel/component/hl7/HL7ByteArrayRouteTest.java
@@ -0,0 +1,219 @@
+/**
+ * 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.hl7;
+
+import ca.uhn.hl7v2.model.Message;
+import ca.uhn.hl7v2.model.v24.message.ADR_A19;
+import ca.uhn.hl7v2.model.v24.message.ADT_A01;
+import ca.uhn.hl7v2.model.v24.message.QRY_A19;
+import ca.uhn.hl7v2.model.v24.segment.MSA;
+import ca.uhn.hl7v2.model.v24.segment.MSH;
+import ca.uhn.hl7v2.model.v24.segment.PID;
+import ca.uhn.hl7v2.model.v24.segment.QRD;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.impl.JndiRegistry;
+import org.apache.camel.spi.DataFormat;
+import org.junit.Test;
+
+/**
+ * Unit test for HL7 routing where the mina endpoint passes on a byte array instead of a
string
+ * and leaves charset interpretation to the dataformat.
+ */
+public class HL7ByteArrayRouteTest extends HL7TestSupport {
+
+    protected JndiRegistry createRegistry() throws Exception {
+        JndiRegistry jndi = super.createRegistry();
+
+        HL7MLLPCodec codec = new HL7MLLPCodec();
+        codec.setProduceString(false);
+
+        jndi.bind("hl7codec", codec);
+
+        MyHL7BusinessLogic logic = new MyHL7BusinessLogic();
+        jndi.bind("hl7service", logic);
+
+        return jndi;
+    }
+
+    @Test
+    public void testSendA19() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:a19");
+        mock.expectedMessageCount(1);
+        mock.message(0).body().isInstanceOf(Message.class);
+
+        String line1 = "MSH|^~\\&|MYSENDER|MYSENDERAPP|MYCLIENT|MYCLIENTAPP|200612211200||QRY^A19|1234|P|2.4";
+        String line2 = "QRD|200612211200|R|I|GetPatient|||1^RD|0101701234|DEM||";
+
+        StringBuilder in = new StringBuilder();
+        in.append(line1);
+        in.append("\r");
+        in.append(line2);
+
+        String out = template.requestBody("mina2:tcp://127.0.0.1:" + getPort() + "?sync=true&codec=#hl7codec",
in.toString(), String.class);
+
+        String[] lines = out.split("\r");
+        assertEquals("MSH|^~\\&|MYSENDER||||200701011539||ADR^A19||||123|||||UNICODE
UTF-8", lines[0]);
+        assertEquals("MSA|AA|123", lines[1]);
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void testSendA01() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:a01");
+        mock.expectedMessageCount(1);
+        mock.message(0).body().isInstanceOf(Message.class);
+
+        String line1 = "MSH|^~\\&|MYSENDER|MYSENDERAPP|MYCLIENT|MYCLIENTAPP|200612211200||ADT^A01|123|P|2.4||||||UNICODE
UTF-8";
+        String line2 = "PID|||123456||Döe^John";
+
+        StringBuilder in = new StringBuilder();
+        in.append(line1);
+        in.append("\r");
+        in.append(line2);
+
+        String out = template.requestBody("mina2:tcp://127.0.0.1:" + getPort() + "?sync=true&codec=#hl7codec",
in.toString(), String.class);
+        String[] lines = out.split("\r");
+        assertEquals("MSH|^~\\&|MYSENDER||||200701011539||ADT^A01||||123|||||UNICODE
UTF-8", lines[0]);
+        assertEquals("PID|||123||Döe^John", lines[1]);
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void testSendUnknown() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:unknown");
+        mock.expectedMessageCount(1);
+        mock.message(0).body().isInstanceOf(Message.class);
+
+        String line1 = "MSH|^~\\&|MYSENDER|MYSENDERAPP|MYCLIENT|MYCLIENTAPP|200612211200||ADT^A02|1234|P|2.4";
+        String line2 = "PID|||123456||Döe^John";
+
+        StringBuilder in = new StringBuilder();
+        in.append(line1);
+        in.append("\r");
+        in.append(line2);
+
+        template.requestBody("mina2:tcp://127.0.0.1:" + getPort() + "?sync=true&codec=#hl7codec",
in.toString());
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            public void configure() throws Exception {
+                // START SNIPPET: e1
+                DataFormat hl7 = new HL7DataFormat();
+                // we setup or HL7 listener on port 8888 (using the hl7codec) and in sync
mode so we can return a response
+                from("mina2:tcp://127.0.0.1:" + getPort() + "?sync=true&codec=#hl7codec")
+                    // we use the HL7 data format to unmarshal from HL7 stream to the HAPI
Message model
+                    // this ensures that the camel message has been enriched with hl7 specific
headers to
+                    // make the routing much easier (see below)
+                    .unmarshal(hl7)
+                    // using choice as the content base router
+                    .choice()
+                        // where we choose that A19 queries invoke the handleA19 method on
our hl7service bean
+                        .when(header("CamelHL7TriggerEvent").isEqualTo("A19"))
+                            .beanRef("hl7service", "handleA19")
+                            .to("mock:a19")
+                        // and A01 should invoke the handleA01 method on our hl7service bean
+                        .when(header("CamelHL7TriggerEvent").isEqualTo("A01")).to("mock:a01")
+                            .beanRef("hl7service", "handleA01")
+                            .to("mock:a19")
+                        // other types should go to mock:unknown
+                        .otherwise()
+                            .to("mock:unknown")
+                    // end choice block
+                    .end()
+                    // marshal response back
+                    .marshal(hl7);
+                // END SNIPPET: e1
+            }
+        };
+    }
+
+    public class MyHL7BusinessLogic {
+
+        // This is a plain POJO that has NO imports whatsoever on Apache Camel.
+        // its a plain POJO only importing the HAPI library so we can much easier work with
the HL7 format.
+
+        public Message handleA19(Message msg) throws Exception {
+            // here you can have your business logic for A19 messages
+            assertTrue(msg instanceof QRY_A19);
+            // just return the same dummy response
+            return createADR19Message();
+        }
+
+        public Message handleA01(Message msg) throws Exception {
+            // here you can have your business logic for A01 messages
+            assertTrue(msg instanceof ADT_A01);
+            // just return the same dummy response
+            return createADT01Message(((ADT_A01)msg).getMSH().getMessageControlID().getValue());
+        }
+    }
+
+    private static Message createADR19Message() throws Exception {
+        ADR_A19 adr = new ADR_A19();
+
+        // Populate the MSH Segment
+        MSH mshSegment = adr.getMSH();
+        mshSegment.getFieldSeparator().setValue("|");
+        mshSegment.getEncodingCharacters().setValue("^~\\&");
+        mshSegment.getDateTimeOfMessage().getTimeOfAnEvent().setValue("200701011539");
+        mshSegment.getSendingApplication().getNamespaceID().setValue("MYSENDER");
+        mshSegment.getSequenceNumber().setValue("123");
+        mshSegment.getMessageType().getMessageType().setValue("ADR");
+        mshSegment.getMessageType().getTriggerEvent().setValue("A19");
+        mshSegment.getCharacterSet(0).setValue("UNICODE UTF-8");
+
+        // Populate the PID Segment
+        MSA msa = adr.getMSA();
+        msa.getAcknowledgementCode().setValue("AA");
+        msa.getMessageControlID().setValue("123");
+
+        QRD qrd = adr.getQRD();
+        qrd.getQueryDateTime().getTimeOfAnEvent().setValue("20080805120000");
+
+        return adr.getMessage();
+    }
+
+    private static Message createADT01Message(String msgId) throws Exception {
+        ADT_A01 adt = new ADT_A01();
+
+        // Populate the MSH Segment
+        MSH mshSegment = adt.getMSH();
+        mshSegment.getFieldSeparator().setValue("|");
+        mshSegment.getEncodingCharacters().setValue("^~\\&");
+        mshSegment.getDateTimeOfMessage().getTimeOfAnEvent().setValue("200701011539");
+        mshSegment.getSendingApplication().getNamespaceID().setValue("MYSENDER");
+        mshSegment.getSequenceNumber().setValue("123");
+        mshSegment.getMessageType().getMessageType().setValue("ADT");
+        mshSegment.getMessageType().getTriggerEvent().setValue("A01");
+        mshSegment.getCharacterSet(0).setValue("UNICODE UTF-8");
+
+        // Populate the PID Segment
+        PID pid = adt.getPID();
+        pid.getPatientName(0).getFamilyName().getSurname().setValue("Döe");
+        pid.getPatientName(0).getGivenName().setValue("John");
+        pid.getPatientIdentifierList(0).getID().setValue(msgId);
+
+        return adt;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/4ba97066/components/camel-hl7/src/test/java/org/apache/camel/component/hl7/HL7DataFormatTest.java
----------------------------------------------------------------------
diff --git a/components/camel-hl7/src/test/java/org/apache/camel/component/hl7/HL7DataFormatTest.java
b/components/camel-hl7/src/test/java/org/apache/camel/component/hl7/HL7DataFormatTest.java
index 8d1444b..4334f9b 100644
--- a/components/camel-hl7/src/test/java/org/apache/camel/component/hl7/HL7DataFormatTest.java
+++ b/components/camel-hl7/src/test/java/org/apache/camel/component/hl7/HL7DataFormatTest.java
@@ -16,6 +16,9 @@
  */
 package org.apache.camel.component.hl7;
 
+import java.io.ByteArrayInputStream;
+import java.nio.charset.Charset;
+
 import ca.uhn.hl7v2.model.Message;
 import ca.uhn.hl7v2.model.v24.message.ADR_A19;
 import ca.uhn.hl7v2.model.v24.segment.MSA;
@@ -34,7 +37,13 @@ public class HL7DataFormatTest extends CamelTestSupport {
     private static final String NONE_ISO_8859_1 = 
         "\u221a\u00c4\u221a\u00e0\u221a\u00e5\u221a\u00ed\u221a\u00f4\u2248\u00ea";
 
-    private HL7DataFormat hl7;
+    private HL7DataFormat hl7 = new HL7DataFormat();
+    private HL7DataFormat hl7big5 = new HL7DataFormat() {
+        @Override
+        protected String guessCharsetName(byte[] b, Exchange exchange) {
+            return "Big5";
+        }
+    };
 
     @Test
     public void testMarshal() throws Exception {
@@ -62,6 +71,22 @@ public class HL7DataFormatTest extends CamelTestSupport {
         template.sendBodyAndProperty("direct:marshal", message, Exchange.CHARSET_NAME, "ISO-8859-1");
         assertMockEndpointsSatisfied();
     }
+
+    @Test
+    public void testMarshalUTF16InMessage() throws Exception {
+        String charsetName = "UTF-16";
+        MockEndpoint mock = getMockEndpoint("mock:marshal");
+        mock.expectedMessageCount(1);
+
+        Message message = createHL7WithCharsetAsMessage(HL7Charset.getHL7Charset(charsetName));
+        template.sendBodyAndProperty("direct:marshal", message, Exchange.CHARSET_NAME, charsetName);
+        assertMockEndpointsSatisfied();
+
+        byte[] body = (byte[])mock.getExchanges().get(0).getIn().getBody();
+        String msg = new String(body, Charset.forName(charsetName));
+        assertTrue(msg.contains("MSA|AA|123"));
+        assertTrue(msg.contains("QRD|20080805120000"));
+    }
     
     @Test
     public void testMarshalUTF8() throws Exception {
@@ -94,6 +119,9 @@ public class HL7DataFormatTest extends CamelTestSupport {
         mock.expectedHeaderReceived(HL7Constants.HL7_PROCESSING_ID, "P");
         mock.expectedHeaderReceived(HL7Constants.HL7_VERSION_ID, "2.4");
         mock.expectedHeaderReceived(HL7Constants.HL7_CONTEXT, hl7.getHapiContext());
+        mock.expectedHeaderReceived(HL7Constants.HL7_CHARSET, null);
+
+        mock.expectedHeaderReceived(Exchange.CHARSET_NAME, "UTF-8");
 
         String body = createHL7AsString();
         template.sendBody("direct:unmarshal", body);
@@ -106,19 +134,68 @@ public class HL7DataFormatTest extends CamelTestSupport {
         assertEquals("0101701234", qrd.getWhoSubjectFilter(0).getIDNumber().getValue());
     }
 
+    @Test
+    public void testUnmarshalWithExplicitUTF16Charset() throws Exception {
+        String charset = "UTF-16";
+        MockEndpoint mock = getMockEndpoint("mock:unmarshal");
+        mock.expectedMessageCount(1);
+        mock.message(0).body().isInstanceOf(Message.class);
+        mock.expectedHeaderReceived(HL7Constants.HL7_CHARSET, HL7Charset.getHL7Charset(charset).getHL7CharsetName());
+        mock.expectedHeaderReceived(Exchange.CHARSET_NAME, charset);
+
+        // Message with explicit encoding in MSH-18
+        byte[] body = createHL7WithCharsetAsString(HL7Charset.UTF_16).getBytes(Charset.forName(charset));
+        template.sendBodyAndHeader("direct:unmarshal", new ByteArrayInputStream(body), Exchange.CHARSET_NAME,
charset);
+
+        assertMockEndpointsSatisfied();
+
+        Message msg = mock.getExchanges().get(0).getIn().getBody(Message.class);
+        assertEquals("2.4", msg.getVersion());
+        QRD qrd = (QRD) msg.get("QRD");
+        assertEquals("0101701234", qrd.getWhoSubjectFilter(0).getIDNumber().getValue());
+    }
+
+    @Test
+    public void testUnmarshalWithImplicitBig5Charset() throws Exception {
+        String charset = "Big5";
+        MockEndpoint mock = getMockEndpoint("mock:unmarshalBig5");
+        mock.expectedMessageCount(1);
+        mock.message(0).body().isInstanceOf(Message.class);
+        mock.expectedHeaderReceived(HL7Constants.HL7_CHARSET, null);
+        mock.expectedHeaderReceived(Exchange.CHARSET_NAME, charset);
+
+        // Message without explicit encoding in MSH-18, but the unmarshaller "guesses"
+        // this time that it is Big5
+        byte[] body = createHL7AsString().getBytes(Charset.forName(charset));
+        template.sendBody("direct:unmarshalBig5", new ByteArrayInputStream(body));
+
+        assertMockEndpointsSatisfied();
+
+        Message msg = mock.getExchanges().get(0).getIn().getBody(Message.class);
+        assertEquals("2.4", msg.getVersion());
+        QRD qrd = (QRD) msg.get("QRD");
+        assertEquals("0101701234", qrd.getWhoSubjectFilter(0).getIDNumber().getValue());
+    }
+
     protected RouteBuilder createRouteBuilder() throws Exception {
 
         return new RouteBuilder() {
             public void configure() throws Exception {
-                hl7 = new HL7DataFormat();
+
                 from("direct:marshal").marshal().hl7().to("mock:marshal");
                 from("direct:unmarshal").unmarshal(hl7).to("mock:unmarshal");
+                from("direct:unmarshalBig5").unmarshal(hl7big5).to("mock:unmarshalBig5");
             }
         };
     }
 
     private static String createHL7AsString() {
-        String line1 = "MSH|^~\\&|MYSENDER|MYSENDERAPP|MYCLIENT|MYCLIENTAPP|200612211200||QRY^A19|1234|P|2.4";
+        return createHL7WithCharsetAsString(null);
+    }
+
+    private static String createHL7WithCharsetAsString(HL7Charset charset) {
+        String hl7Charset = charset == null ? "" : charset.getHL7CharsetName();
+        String line1 = String.format("MSH|^~\\&|MYSENDER|MYSENDERAPP|MYCLIENT|MYCLIENTAPP|200612211200||QRY^A19|1234|P|2.4||||||%s",
hl7Charset);
         String line2 = "QRD|200612211200|R|I|GetPatient|||1^RD|0101701234|DEM||";
 
         StringBuilder body = new StringBuilder();
@@ -128,7 +205,7 @@ public class HL7DataFormatTest extends CamelTestSupport {
         return body.toString();
     }
 
-    private static Message createHL7AsMessage() throws Exception {
+    private static ADR_A19 createHL7AsMessage() throws Exception {
         ADR_A19 adr = new ADR_A19();
        
         // Populate the MSH Segment
@@ -150,7 +227,13 @@ public class HL7DataFormatTest extends CamelTestSupport {
         QRD qrd = adr.getQRD();
         qrd.getQueryDateTime().getTimeOfAnEvent().setValue("20080805120000");
 
-        return adr.getMessage();
+        return adr;
+    }
+
+    private static ADR_A19 createHL7WithCharsetAsMessage(HL7Charset charset) throws Exception
{
+        ADR_A19 adr = createHL7AsMessage();
+        adr.getMSH().getCharacterSet(0).setValue(charset.getHL7CharsetName());
+        return adr;
     }
 
 }


Mime
View raw message