package org.wso2.carbon.inbound.endpoint.protocol.hl7.util; /** * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. 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. */ import ca.uhn.hl7v2.AcknowledgmentCode; import ca.uhn.hl7v2.DefaultHapiContext; import ca.uhn.hl7v2.HL7Exception; import ca.uhn.hl7v2.HapiContext; import ca.uhn.hl7v2.model.DataTypeException; import ca.uhn.hl7v2.model.Message; import ca.uhn.hl7v2.model.v22.message.ACK; import ca.uhn.hl7v2.parser.*; import ca.uhn.hl7v2.util.idgenerator.UUIDGenerator; import ca.uhn.hl7v2.validation.impl.DefaultValidation; import ca.uhn.hl7v2.validation.impl.NoValidation; import org.apache.axiom.om.*; import org.apache.axiom.om.impl.dom.factory.OMDOMFactory; import org.apache.axiom.om.util.AXIOMUtil; import org.apache.axiom.soap.SOAPEnvelope; import org.apache.axiom.soap.SOAPFactory; import org.apache.axiom.util.UIDGenerator; import org.apache.axis2.AxisFault; import org.apache.axis2.context.ConfigurationContext; import org.apache.axis2.context.OperationContext; import org.apache.axis2.context.ServiceContext; import org.apache.axis2.description.InOutAxisOperation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.synapse.MessageContext; import org.apache.synapse.SynapseConstants; import org.apache.synapse.core.axis2.MessageContextCreatorForAxis2; import org.apache.synapse.inbound.InboundProcessorParams; import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.core.multitenancy.utils.TenantAxisUtils; import org.wso2.carbon.inbound.endpoint.osgi.service.ServiceReferenceHolder; import org.wso2.carbon.inbound.endpoint.protocol.hl7.core.MLLPConstants; import org.wso2.carbon.inbound.endpoint.protocol.hl7.core.MLLProtocolException; import org.wso2.carbon.utils.multitenancy.MultitenantConstants; import javax.xml.namespace.QName; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import java.io.IOException; import java.util.NoSuchElementException; public class HL7MessageUtils { private static final Log log = LogFactory.getLog(HL7MessageUtils.class); private static HapiContext validationContext = new DefaultHapiContext(); private static HapiContext noValidationContext = new DefaultHapiContext(); private static ConfigurationContext context; static { noValidationContext.setValidationContext(new NoValidation()); validationContext.setValidationContext(new DefaultValidation()); } private static PipeParser noValidationPipeParser = new PipeParser(noValidationContext); private static XMLParser noValidationXmlParser = new DefaultXMLParser(noValidationContext); private static PipeParser pipeParser = new PipeParser(validationContext); private static XMLParser xmlParser = new DefaultXMLParser(validationContext); private static SOAPFactory fac = OMAbstractFactory.getSOAP11Factory(); private static OMNamespace ns = fac.createOMNamespace(Axis2HL7Constants.HL7_NAMESPACE, Axis2HL7Constants.HL7_ELEMENT_NAME); private static OMFactory omFactory = new OMDOMFactory(); static { if (HL7Configuration.getInstance().getStringProperty(MLLPConstants.HL7_ID_GENERATOR, "file").equals("uuid")) { validationContext.getParserConfiguration().setIdGenerator(new UUIDGenerator()); noValidationContext.getParserConfiguration().setIdGenerator(new UUIDGenerator()); } } public static Message parse(String msg, boolean validate) throws HL7Exception { if (validate) { return pipeParser.parse(msg); } else { return noValidationPipeParser.parse(msg); } } public static Message parse(String msg, final Parser preProcessor) throws HL7Exception { return preProcessor.parse(msg); } public static MessageContext createSynapseMessageContext(Message message, InboundProcessorParams params) throws HL7Exception, AxisFault { MessageContext synCtx = createSynapseMessageContext(params.getProperties() .getProperty(MLLPConstants.HL7_INBOUND_TENANT_DOMAIN)); if (params.getProperties().getProperty(Axis2HL7Constants.HL7_VALIDATION_PASSED) != null) { synCtx.setProperty(Axis2HL7Constants.HL7_VALIDATION_PASSED, params.getProperties().getProperty(Axis2HL7Constants.HL7_VALIDATION_PASSED)); } try { synCtx.setEnvelope(createEnvelope(synCtx, message, params)); } catch (Exception e) { throw new HL7Exception(e); } return synCtx; } public static MessageContext createErrorMessageContext(String rawMessage, Exception errorMsg, InboundProcessorParams params) throws AxisFault, HL7Exception { MessageContext synCtx = createSynapseMessageContext(params.getProperties() .getProperty(MLLPConstants.HL7_INBOUND_TENANT_DOMAIN)); if (params.getProperties().getProperty(Axis2HL7Constants.HL7_VALIDATION_PASSED) != null) { synCtx.setProperty(Axis2HL7Constants.HL7_VALIDATION_PASSED, params.getProperties().getProperty(Axis2HL7Constants.HL7_VALIDATION_PASSED)); } try { synCtx.setProperty(SynapseConstants.ERROR_CODE, SynapseConstants.RCV_IO_ERROR_RECEIVING); synCtx.setProperty(SynapseConstants.ERROR_MESSAGE, errorMsg.getMessage()); synCtx.setProperty(SynapseConstants.ERROR_DETAIL, (errorMsg.getCause() == null ? "null" : errorMsg.getCause().getMessage())); synCtx.setProperty(SynapseConstants.ERROR_EXCEPTION, errorMsg); synCtx.setEnvelope(createErrorEnvelope(synCtx, rawMessage, errorMsg.getMessage(), params)); } catch (Exception e) { throw new HL7Exception(e); } return synCtx; } // Create Synapse Message Context private static org.apache.synapse.MessageContext createSynapseMessageContext(String tenantDomain) throws AxisFault { // Create super tenant message context org.apache.axis2.context.MessageContext axis2MsgCtx = createAxis2MessageContext(); ServiceContext svcCtx = new ServiceContext(); OperationContext opCtx = new OperationContext(new InOutAxisOperation(), svcCtx); axis2MsgCtx.setServiceContext(svcCtx); axis2MsgCtx.setOperationContext(opCtx); // If not super tenant, assign tenant configuration context if (!tenantDomain.equals(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME)) { ConfigurationContext tenantConfigCtx = TenantAxisUtils.getTenantConfigurationContext(tenantDomain, axis2MsgCtx.getConfigurationContext()); axis2MsgCtx.setConfigurationContext(tenantConfigCtx); axis2MsgCtx.setProperty(MultitenantConstants.TENANT_DOMAIN, tenantDomain); } return MessageContextCreatorForAxis2.getSynapseMessageContext(axis2MsgCtx); } // Create Axis2 Message Context private static org.apache.axis2.context.MessageContext createAxis2MessageContext() { org.apache.axis2.context.MessageContext axis2MsgCtx = new org.apache.axis2.context.MessageContext(); axis2MsgCtx.setMessageID(UIDGenerator.generateURNString()); axis2MsgCtx.setConfigurationContext(ServiceReferenceHolder.getInstance().getConfigurationContextService() .getServerConfigContext()); // Axis2 spawns a new threads to send a message if this is TRUE axis2MsgCtx.setProperty(org.apache.axis2.context.MessageContext.CLIENT_API_NON_BLOCKING, Boolean.FALSE); axis2MsgCtx.setServerSide(true); return axis2MsgCtx; } private static String getTenantDomain() { return PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); } private static SOAPEnvelope createEnvelope(MessageContext synCtx, Message message, InboundProcessorParams params) throws HL7Exception, XMLStreamException, MLLProtocolException { SOAPEnvelope envelope = fac.getDefaultEnvelope(); boolean rawMessage = false; String xmlDoc = ""; try { xmlDoc = xmlParser.encode(message); synCtx.setProperty(Axis2HL7Constants.HL7_VALIDATION_PASSED, new Boolean(true)); } catch (HL7Exception e) { synCtx.setProperty(Axis2HL7Constants.HL7_VALIDATION_PASSED, new Boolean(false)); if (params.getProperties().getProperty(MLLPConstants.PARAM_HL7_BUILD_RAW_MESSAGE) != null && params.getProperties().getProperty(MLLPConstants.PARAM_HL7_BUILD_RAW_MESSAGE).equals("true")) { xmlDoc = message.encode(); rawMessage = true; } else { log.error("Could not encode HL7 message into XML. " + "Set " + MLLPConstants.PARAM_HL7_BUILD_RAW_MESSAGE + " to build invalid HL7 messages containing raw HL7 message.", e); throw new HL7Exception("Could not encode HL7 message into XML", e); } } OMElement messageEl; if (!rawMessage) { messageEl = generateHL7MessageElement(xmlDoc); } else { messageEl = generateHL7RawMessaegElement(xmlDoc); } envelope.getBody().addChild(messageEl); return envelope; } private static SOAPEnvelope createErrorEnvelope(MessageContext synCtx, String rawMsg, String erroMsg, InboundProcessorParams params) { SOAPEnvelope envelope = fac.getDefaultEnvelope(); OMElement messageEl; boolean rawMessage = false; if (params.getProperties().getProperty(MLLPConstants.PARAM_HL7_BUILD_RAW_MESSAGE) != null && params.getProperties().getProperty(MLLPConstants.PARAM_HL7_BUILD_RAW_MESSAGE).equals("true")) { rawMessage = true; } if (rawMessage) { messageEl = generateHL7RawMessaegElement(rawMsg); envelope.getBody().addChild(messageEl); } return envelope; } public static OMElement generateHL7MessageElement(String hl7XmlMessage) throws XMLStreamException { OMElement hl7Element = AXIOMUtil.stringToOM(hl7XmlMessage); OMElement messageEl = fac.createOMElement(Axis2HL7Constants.HL7_MESSAGE_ELEMENT_NAME, ns); messageEl.addChild(hl7Element); return messageEl; } public static OMElement generateHL7RawMessaegElement(String hl7XmlMessage) { OMElement hl7Element = omFactory.createOMElement(new QName("rawMessage")); OMText rawMessage = hl7Element.getOMFactory().createOMText(hl7Element, hl7XmlMessage, XMLStreamConstants.CDATA); hl7Element.addChild(rawMessage); OMElement messageEl = fac.createOMElement(Axis2HL7Constants.HL7_MESSAGE_ELEMENT_NAME, ns); messageEl.addChild(hl7Element); return messageEl; } public static Message createNack(Message hl7Msg, String errorMsg) throws HL7Exception { if (errorMsg == null) { errorMsg = ""; } if (hl7Msg == null) { return createDefaultNackMessage(errorMsg); } else { try { return hl7Msg.generateACK(AcknowledgmentCode.AE, new HL7Exception(errorMsg)); } catch (IOException e) { throw new HL7Exception(e); } } } public static Message createDefaultNack(String errorMsg) { try { return createDefaultNackMessage(errorMsg); } catch (DataTypeException e) { log.error("Error while creating default NACK message.", e); } return null; } private static Message createDefaultNackMessage(String errorMsg) throws DataTypeException { ACK ack = new ACK(); ack.getMSH().getFieldSeparator().setValue( Axis2HL7Constants.HL7_DEFAULT_FIELD_SEPARATOR); ack.getMSH().getEncodingCharacters().setValue( Axis2HL7Constants.HL7_DEFAULT_ENCODING_CHARS); ack.getMSH().getReceivingApplication().setValue( Axis2HL7Constants.HL7_DEFAULT_RECEIVING_APPLICATION); ack.getMSH().getReceivingFacility().setValue( Axis2HL7Constants.HL7_DEFAULT_RECEIVING_FACILITY); ack.getMSH().getProcessingID().setValue( Axis2HL7Constants.HL7_DEFAULT_PROCESSING_ID); ack.getMSA().getAcknowledgementCode().setValue(Axis2HL7Constants.HL7_DEFAULT_ACK_CODE_AR); ack.getMSA().getMessageControlID().setValue(Axis2HL7Constants.HL7_DEFAULT_MESSAGE_CONTROL_ID); ack.getERR().getErrorCodeAndLocation(0).getCodeIdentifyingError(). getIdentifier().setValue(errorMsg); return ack; } public static int getInt(String key, InboundProcessorParams params) throws NumberFormatException { return Integer.valueOf(params.getProperties().getProperty(key)); } public static boolean getBoolean(String key, InboundProcessorParams params) { return Boolean.valueOf(params.getProperties().getProperty(key)); } /** * Get the hl7message from the MessageContext * @param ctx * @return * @throws HL7Exception */ public static Message payloadToHL7Message(MessageContext ctx, InboundProcessorParams params) throws HL7Exception, NoSuchElementException { OMElement hl7MsgEl = (OMElement) ctx.getEnvelope().getBody().getChildrenWithName(new QName(Axis2HL7Constants.HL7_NAMESPACE, Axis2HL7Constants.HL7_MESSAGE_ELEMENT_NAME)) .next(); String hl7XMLPayload = hl7MsgEl.getFirstElement().toString(); String pipeMsg; Message msg = null; try { msg = xmlParser.parse(hl7XMLPayload); pipeMsg = pipeParser.encode(msg); msg = pipeParser.parse(pipeMsg); return msg; } catch (EncodingNotSupportedException e) { log.error("Encoding error in the message",e); throw new HL7Exception("Encoding error in the message: " + e.getMessage(), e); } catch (DataTypeException e) { // Make this as warning.Since some remote systems require enriched messages that violate some HL7 //rules it would be nice to be able to still send the message. log.warn("Rule validation fails.", e); if (!(params.getProperties().getProperty(Axis2HL7Constants.HL7_VALIDATE_MESSAGE).equals("false"))) { msg = noValidationXmlParser.parse(hl7XMLPayload); return msg; } } catch (HL7Exception e) { log.error("Error in the Message :" , e); throw new HL7Exception("Encoding error in the message: " + e.getMessage(), e); } return msg; } }