All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.camel.component.mllp.MllpTcpServerConsumer Maven / Gradle / Ivy

The newest version!
/*
 * 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.io.IOException;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

import org.apache.camel.Exchange;
import org.apache.camel.ExchangePattern;
import org.apache.camel.ExchangePropertyKey;
import org.apache.camel.Message;
import org.apache.camel.Processor;
import org.apache.camel.RuntimeCamelException;
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.internal.Hl7Util;
import org.apache.camel.component.mllp.internal.MllpSocketBuffer;
import org.apache.camel.component.mllp.internal.TcpServerAcceptThread;
import org.apache.camel.component.mllp.internal.TcpServerBindThread;
import org.apache.camel.component.mllp.internal.TcpServerConsumerValidationRunnable;
import org.apache.camel.component.mllp.internal.TcpSocketConsumerRunnable;
import org.apache.camel.processor.mllp.Hl7AcknowledgementGenerationException;
import org.apache.camel.support.DefaultConsumer;
import org.apache.camel.support.ExchangeHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The MLLP consumer.
 */
@ManagedResource(description = "MLLP Producer")
public class MllpTcpServerConsumer extends DefaultConsumer {
    final Logger log;
    final ExecutorService validationExecutor;
    final ExecutorService consumerExecutor;
    final Charset charset;
    final Hl7Util hl7Util;
    final boolean logPhi;

    TcpServerBindThread bindThread;
    TcpServerAcceptThread acceptThread;

    Map consumerRunnables = new ConcurrentHashMap<>();

    public MllpTcpServerConsumer(MllpEndpoint endpoint, Processor processor) {
        super(endpoint, processor);
        log = LoggerFactory.getLogger(String.format("%s.%d", this.getClass().getName(), endpoint.getPort()));
        charset = Charset.forName(endpoint.getConfiguration().getCharsetName());
        MllpComponent component = endpoint.getComponent();
        this.logPhi = component.getLogPhi();
        hl7Util = new Hl7Util(component.getLogPhiMaxBytes(), logPhi);

        validationExecutor = Executors.newCachedThreadPool();
        consumerExecutor = new ThreadPoolExecutor(
                1, getConfiguration().getMaxConcurrentConsumers(), getConfiguration().getAcceptTimeout(), TimeUnit.MILLISECONDS,
                new SynchronousQueue<>());
    }

    @Override
    public boolean isHostedService() {
        return true;
    }

    @ManagedAttribute(description = "Last activity time")
    public Map getLastActivityTimes() {
        Map answer = new HashMap<>();

        for (Map.Entry entry : consumerRunnables.entrySet()) {
            TcpSocketConsumerRunnable consumerRunnable = entry.getKey();
            if (consumerRunnable != null) {
                answer.put(consumerRunnable.getCombinedAddress(), new Date(entry.getValue()));
            }
        }
        return answer;
    }

    @ManagedOperation(description = "Close Connections")
    public void closeConnections() {
        for (TcpSocketConsumerRunnable consumerRunnable : consumerRunnables.keySet()) {
            if (consumerRunnable != null) {
                log.info("Close Connection called via JMX for address {}", consumerRunnable.getCombinedAddress());
                consumerRunnable.closeSocket();
            }
        }
    }

    @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();
            }
        }
    }

    @Override
    public MllpEndpoint getEndpoint() {
        return (MllpEndpoint) super.getEndpoint();
    }

    @Override
    protected void doStop() throws Exception {
        log.trace("doStop()");

        // Close any client sockets that are currently open
        for (TcpSocketConsumerRunnable consumerClientSocketThread : consumerRunnables.keySet()) {
            consumerClientSocketThread.stop();
        }

        if (acceptThread != null) {
            acceptThread.interrupt();
            acceptThread = null;
        }

        if (bindThread != null) {
            bindThread.interrupt();
            bindThread = null;
        }

        super.doStop();
    }

    @Override
    protected void doStart() throws Exception {
        if (bindThread == null || !bindThread.isAlive()) {
            bindThread = new TcpServerBindThread(this);

            if (getConfiguration().isLenientBind()) {
                log.debug("doStart() - starting bind thread");
                bindThread.start();
            } else {
                log.debug("doStart() - attempting to bind to port {}", getEndpoint().getPort());
                bindThread.run();

                if (this.acceptThread == null) {
                    throw new BindException("Failed to bind to port " + getEndpoint().getPort());
                }
            }
        }

        super.doStart();
    }

    @Override
    protected void doShutdown() throws Exception {
        super.doShutdown();
        consumerExecutor.shutdownNow();
        if (acceptThread != null) {
            acceptThread.interrupt();
        }
        validationExecutor.shutdownNow();
    }

    public void handleMessageTimeout(String message, byte[] payload, Throwable cause) {
        getExceptionHandler().handleException(new MllpInvalidMessageException(message, payload, cause, logPhi));
    }

    public void handleMessageException(String message, byte[] payload, Throwable cause) {
        getExceptionHandler().handleException(new MllpReceiveException(message, payload, cause, logPhi));
    }

    public MllpConfiguration getConfiguration() {
        return getEndpoint().getConfiguration();
    }

    public Map getConsumerRunnables() {
        return consumerRunnables;
    }

    public void validateConsumer(Socket clientSocket) {
        MllpSocketBuffer mllpBuffer = new MllpSocketBuffer(getEndpoint());
        TcpServerConsumerValidationRunnable client = new TcpServerConsumerValidationRunnable(this, clientSocket, mllpBuffer);

        try {
            log.debug("validateConsumer({}) - submitting client for validation", clientSocket);
            validationExecutor.submit(client);
        } catch (RejectedExecutionException rejectedExecutionEx) {
            log.warn("validateConsumer({}) - cannot validate client - max validations already active", clientSocket);
            mllpBuffer.resetSocket(clientSocket);
        }
    }

    public void startAcceptThread(ServerSocket serverSocket) {
        acceptThread = new TcpServerAcceptThread(this, serverSocket);
        acceptThread.start();
    }

    public void startConsumer(Socket clientSocket, MllpSocketBuffer mllpBuffer) {
        TcpSocketConsumerRunnable client = new TcpSocketConsumerRunnable(
                this, clientSocket, mllpBuffer, hl7Util, logPhi);

        consumerRunnables.put(client, System.currentTimeMillis());
        try {
            log.info("startConsumer({}) - starting consumer", clientSocket);
            consumerExecutor.submit(client);
            getEndpoint().updateLastConnectionEstablishedTicks();
        } catch (RejectedExecutionException rejectedExecutionEx) {
            log.warn("startConsumer({}) - cannot start consumer - max consumers already active", clientSocket);
            mllpBuffer.resetSocket(clientSocket);
        }
    }

    public void processMessage(byte[] hl7MessageBytes, TcpSocketConsumerRunnable consumerRunnable) {
        long now = System.currentTimeMillis();

        getEndpoint().updateLastConnectionActivityTicks(now);
        consumerRunnables.put(consumerRunnable, now);

        // Send the message on to Camel for processing and wait for the response
        log.debug("processMessage(hl7MessageBytes[{}], {}) - populating the exchange with received payload",
                hl7MessageBytes == null ? -1 : hl7MessageBytes.length, consumerRunnable.getSocket());
        Exchange exchange = createExchange(false);
        exchange.setPattern(ExchangePattern.InOut);
        if (getConfiguration().hasCharsetName()) {
            exchange.setProperty(ExchangePropertyKey.CHARSET_NAME, getConfiguration().getCharsetName());
        }
        try {
            createUoW(exchange);

            if (exchange.hasProperties() || exchange.getProperty(MllpConstants.MLLP_AUTO_ACKNOWLEDGE) == null) {
                exchange.setProperty(MllpConstants.MLLP_AUTO_ACKNOWLEDGE, getConfiguration().isAutoAck());
            }

            Message message = exchange.getIn();

            if (consumerRunnable.hasLocalAddress()) {
                message.setHeader(MllpConstants.MLLP_LOCAL_ADDRESS, consumerRunnable.getLocalAddress());
            }

            if (consumerRunnable.hasRemoteAddress()) {
                message.setHeader(MllpConstants.MLLP_REMOTE_ADDRESS, consumerRunnable.getRemoteAddress());
            }

            if (getConfiguration().isValidatePayload()) {
                String exceptionMessage = hl7Util.generateInvalidPayloadExceptionMessage(hl7MessageBytes);
                if (exceptionMessage != null) {
                    exchange.setException(new MllpInvalidMessageException(exceptionMessage, hl7MessageBytes, logPhi));
                }
            }
            populateHl7DataHeaders(exchange, message, hl7MessageBytes);

            if (getConfiguration().isStringPayload()) {
                if (hl7MessageBytes != null && hl7MessageBytes.length > 0) {
                    message.setBody(
                            new String(
                                    hl7MessageBytes,
                                    MllpCharsetHelper.getCharset(exchange, hl7MessageBytes, hl7Util, charset)));
                } else {
                    message.setBody("", String.class);
                }
            } else {
                message.setBody(hl7MessageBytes, byte[].class);
            }

            log.debug("processMessage(hl7MessageBytes[{}], {}) - calling processor",
                    hl7MessageBytes == null ? -1 : hl7MessageBytes.length, consumerRunnable.getSocket());
            try {
                getProcessor().process(exchange);
                sendAcknowledgement(hl7MessageBytes, exchange, consumerRunnable);
            } catch (Exception unexpectedEx) {
                String resetMessage
                        = "processMessage(byte[], TcpSocketConsumerRunnable) - Unexpected exception processing exchange";
                consumerRunnable.resetSocket(resetMessage);
                getExceptionHandler().handleException(resetMessage, exchange, unexpectedEx);
            }
        } catch (Exception uowEx) {
            getExceptionHandler().handleException(
                    "processMessage(byte[], TcpSocketConsumerRunnable) - Unexpected exception creating Unit of Work", exchange,
                    uowEx);
        } finally {
            doneUoW(exchange);
            releaseExchange(exchange, false);
        }
    }

    void populateHl7DataHeaders(Exchange exchange, Message message, byte[] hl7MessageBytes) {
        if (getConfiguration().isHl7Headers() && 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 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.warn("Population of message headers failed - unable to find the end of the MSH segment");
            } else {
                log.debug("Populating the HL7 message headers");
                Charset charset = MllpCharsetHelper.getCharset(exchange, this.charset);

                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;
                        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 = (i == 17 && getConfiguration().hasCharsetName())
                                ? getConfiguration().getCharsetName()
                                : 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(Pattern.quote(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 sendAcknowledgement(byte[] originalHl7MessageBytes, Exchange exchange, TcpSocketConsumerRunnable consumerRunnable) {
        log.trace("sendAcknowledgement(originalHl7MessageBytes[{}], Exchange[{}], {}) - entering",
                originalHl7MessageBytes == null ? -1 : originalHl7MessageBytes.length, exchange.getExchangeId(),
                consumerRunnable.getSocket());

        getEndpoint().checkBeforeSendProperties(exchange, consumerRunnable.getSocket(), 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) {
                if (getConfiguration().getExchangePattern() == ExchangePattern.InOut) {
                    final Object acknowledgementBytesProperty = exchange.getProperty(MllpConstants.MLLP_ACKNOWLEDGEMENT);
                    Object acknowledgementStringProperty = exchange.getProperty(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING);
                    final String exceptionMessage
                            = (acknowledgementBytesProperty == null && acknowledgementStringProperty == null)
                                    ? "Automatic Acknowledgement is disabled and the "
                                      + MllpConstants.MLLP_ACKNOWLEDGEMENT + " and " + MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING
                                      + " exchange properties are null"
                                    : "Automatic Acknowledgement is disabled and neither the "
                                      + MllpConstants.MLLP_ACKNOWLEDGEMENT + "(type = "
                                      + getTypeOrNullString(acknowledgementBytesProperty) + ") nor the"
                                      + MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING + "(type = "
                                      + getTypeOrNullString(acknowledgementBytesProperty)
                                      + ") exchange properties can be converted to byte[]";
                    MllpInvalidAcknowledgementException invalidAckEx = new MllpInvalidAcknowledgementException(
                            exceptionMessage, originalHl7MessageBytes, acknowledgementMessageBytes, logPhi);
                    exchange.setProperty(MllpConstants.MLLP_ACKNOWLEDGEMENT_EXCEPTION, invalidAckEx);
                    getExceptionHandler().handleException(invalidAckEx);
                }
            } else {
                String acknowledgmentTypeProperty = exchange.getProperty(MllpConstants.MLLP_ACKNOWLEDGEMENT_TYPE, String.class);
                String msa3 = exchange.getProperty(MllpConstants.MLLP_ACKNOWLEDGEMENT_MSA_TEXT, String.class);
                Exception exchangeEx = exchange.getException();

                try {
                    if (null == acknowledgmentTypeProperty) {
                        if (null == exchangeEx) {
                            acknowledgementMessageType = "AA";
                        } else {
                            acknowledgementMessageType = "AE";
                            if (msa3 == null || msa3.isEmpty()) {
                                msa3 = exchangeEx.getClass().getName();
                            }
                        }
                    } else {
                        switch (acknowledgmentTypeProperty) {
                            case "AA":
                                acknowledgementMessageType = "AA";
                                break;
                            case "AE":
                                acknowledgementMessageType = "AE";
                                if (exchangeEx != null && msa3 != null && msa3.isEmpty()) {
                                    msa3 = exchangeEx.getClass().getName();
                                }
                                break;
                            case "AR":
                                acknowledgementMessageType = "AR";
                                if (exchangeEx != null && msa3 != null && msa3.isEmpty()) {
                                    msa3 = exchangeEx.getClass().getName();
                                }
                                break;
                            default:
                                exchange.setException(new Hl7AcknowledgementGenerationException(
                                        hl7Util,
                                        "Unsupported acknowledgment type: " + acknowledgmentTypeProperty));
                                return;
                        }
                    }

                    hl7Util.generateAcknowledgementPayload(consumerRunnable.getMllpBuffer(), originalHl7MessageBytes,
                            acknowledgementMessageType, msa3);

                } catch (MllpAcknowledgementGenerationException ackGenerationException) {
                    exchange.setProperty(MllpConstants.MLLP_ACKNOWLEDGEMENT_EXCEPTION, ackGenerationException);
                    getExceptionHandler().handleException(ackGenerationException);
                }
            }
        } else {
            consumerRunnable.getMllpBuffer().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
                                    = new String(acknowledgmentTypeBytes, ExchangeHelper.getCharsetName(exchange));
                        } catch (IOException ioEx) {
                            throw new RuntimeCamelException("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 [{}] found in message - should be AA, AE or AR",
                                            acknowledgementMessageType);
                            }
                        }

                        // 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 [{}] does not match {} exchange property "
                                     + "value [{}] - using value found in message",
                                    acknowledgementMessageType, MllpConstants.MLLP_ACKNOWLEDGEMENT_TYPE,
                                    acknowledgementTypeProperty);
                        }
                    }
                }
            }
        }

        Message message = exchange.getMessage();
        if (acknowledgementMessageType != null && !acknowledgementMessageType.isEmpty()) {
            message.setHeader(MllpConstants.MLLP_ACKNOWLEDGEMENT_TYPE, acknowledgementMessageType);
        }

        Charset charset = MllpCharsetHelper.getCharset(exchange, this.charset);

        // TODO:  re-evaluate this - it seems that the MLLP buffer should be populated by now
        if (consumerRunnable.getMllpBuffer().hasCompleteEnvelope()) {
            // The mllpBuffer will be used if bufferWrites is set or if auto acknowledgement is used
            message.setHeader(MllpConstants.MLLP_ACKNOWLEDGEMENT, consumerRunnable.getMllpBuffer().toMllpPayload());
            message.setHeader(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING, consumerRunnable.getMllpBuffer().toHl7String(charset));

            // Send the acknowledgement
            if (log.isDebugEnabled()) {
                log.debug("sendAcknowledgement(originalHl7MessageBytes[{}], Exchange[{}], {}) - Sending Acknowledgement: {}",
                        originalHl7MessageBytes == null ? -1 : originalHl7MessageBytes.length, exchange.getExchangeId(),
                        consumerRunnable.getSocket(),
                        consumerRunnable.getMllpBuffer().toPrintFriendlyHl7String());
            }

            try {
                consumerRunnable.getMllpBuffer().writeTo(consumerRunnable.getSocket());
            } catch (MllpSocketException acknowledgementDeliveryEx) {
                Exception exchangeEx = new MllpAcknowledgementDeliveryException(
                        "Failure delivering acknowledgment", originalHl7MessageBytes, acknowledgementMessageBytes,
                        acknowledgementDeliveryEx, logPhi);
                exchange.setProperty(MllpConstants.MLLP_ACKNOWLEDGEMENT_EXCEPTION, acknowledgementDeliveryEx);
                exchange.setException(exchangeEx);
            } finally {
                consumerRunnable.getMllpBuffer().reset();
            }
        } else if (acknowledgementMessageBytes != null && acknowledgementMessageBytes.length > 0) {
            message.setHeader(MllpConstants.MLLP_ACKNOWLEDGEMENT, acknowledgementMessageBytes);
            String acknowledgementMessageString = new String(acknowledgementMessageBytes, charset);
            message.setHeader(MllpConstants.MLLP_ACKNOWLEDGEMENT_STRING, acknowledgementMessageString);

            // Send the acknowledgement
            if (log.isDebugEnabled()) {
                log.debug("sendAcknowledgement(originalHl7MessageBytes[{}], Exchange[{}], {}) - Sending Acknowledgement: {}",
                        originalHl7MessageBytes == null ? -1 : originalHl7MessageBytes.length, exchange.getExchangeId(),
                        consumerRunnable.getSocket(),
                        hl7Util.convertToPrintFriendlyString(acknowledgementMessageBytes));
            }

            try {
                consumerRunnable.getMllpBuffer().setEnvelopedMessage(acknowledgementMessageBytes);
                consumerRunnable.getMllpBuffer().writeTo(consumerRunnable.getSocket());
            } catch (MllpSocketException acknowledgementDeliveryEx) {
                Exception exchangeEx = new MllpAcknowledgementDeliveryException(
                        "Failure delivering acknowledgment", originalHl7MessageBytes, acknowledgementMessageBytes,
                        acknowledgementDeliveryEx, logPhi);
                exchange.setProperty(MllpConstants.MLLP_ACKNOWLEDGEMENT_EXCEPTION, acknowledgementDeliveryEx);
                exchange.setException(exchangeEx);
            }
        }

        getEndpoint().checkAfterSendProperties(exchange, consumerRunnable.getSocket(), log);
    }

    private static String getTypeOrNullString(Object acknowledgementBytesProperty) {
        if (acknowledgementBytesProperty != null) {
            return acknowledgementBytesProperty.getClass().getSimpleName();
        }

        return "null";
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy