/**
* 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.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.ManagedResource;
import org.apache.camel.impl.DefaultEndpoint;
import org.apache.camel.spi.Metadata;
import org.apache.camel.spi.UriEndpoint;
import org.apache.camel.spi.UriParam;
import org.apache.camel.spi.UriPath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Provides functionality required by Healthcare providers to communicate with other systems using the MLLP protocol.
* <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")
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
private static final Logger LOG = LoggerFactory.getLogger(MllpEndpoint.class);
@UriPath @Metadata(required = "true")
String hostname;
@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;
@UriParam(defaultValue = "true")
boolean bufferWrites = true;
@UriParam(defaultValue = "false")
boolean validatePayload;
@UriParam(label = "codec")
String charsetName;
public MllpEndpoint(String uri, MllpComponent component) {
super(uri, component);
}
@Override
public ExchangePattern getExchangePattern() {
return ExchangePattern.InOut;
}
@Override
public Exchange createExchange(ExchangePattern exchangePattern) {
Exchange mllpExchange = super.createExchange(exchangePattern);
setExchangeProperties(mllpExchange);
return mllpExchange;
}
@Override
public Exchange createExchange(Exchange exchange) {
Exchange mllpExchange = super.createExchange(exchange);
setExchangeProperties(mllpExchange);
return mllpExchange;
}
private void setExchangeProperties(Exchange mllpExchange) {
if (charsetName != null) {
mllpExchange.setProperty(Exchange.CHARSET_NAME, charsetName);
}
}
public Producer createProducer() throws Exception {
LOG.trace("({}).createProducer()", this.getEndpointKey());
return new MllpTcpClientProducer(this);
}
public Consumer createConsumer(Processor processor) throws Exception {
LOG.trace("({}).createConsumer(processor)", this.getEndpointKey());
Consumer consumer = new MllpTcpServerConsumer(this, processor);
configureConsumer(consumer);
return consumer;
}
@Override
public boolean isSynchronous() {
return true;
}
public boolean isSingleton() {
return true;
}
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 String getHostname() {
return hostname;
}
/**
* Hostname or IP for connection for the TCP connection.
*
* The default value is null, which means any local IP address
*
* @param hostname Hostname or IP
*/
public void setHostname(String hostname) {
this.hostname = hostname;
}
public int getPort() {
return port;
}
/**
* Port number for the TCP connection
*
* @param port TCP port
*/
public void setPort(int port) {
this.port = port;
}
public int 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(int 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 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
*/
public void setMaxReceiveTimeouts(int maxReceiveTimeouts) {
this.maxReceiveTimeouts = maxReceiveTimeouts;
}
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 isKeepAlive() {
return keepAlive;
}
/**
* 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 boolean isTcpNoDelay() {
return tcpNoDelay;
}
/**
* 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 boolean isReuseAddress() {
return reuseAddress;
}
/**
* 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 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 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;
}
/**
* 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 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
*
* 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;
}
}