oscar.oscarEncounter.oscarMeasurements.hl7.MeasurementHL7UploaderAction.java Source code

Java tutorial

Introduction

Here is the source code for oscar.oscarEncounter.oscarMeasurements.hl7.MeasurementHL7UploaderAction.java

Source

/**
 * Copyright (c) 2006-. OSCARservice, OpenSoft System. All Rights Reserved.
 * This software is published under the GPL GNU General Public License.
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

package oscar.oscarEncounter.oscarMeasurements.hl7;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.PublicKey;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.upload.FormFile;
import org.oscarehr.common.model.Demographic;
import org.oscarehr.util.SpringUtils;
import org.springframework.web.struts.DispatchActionSupport;

import oscar.OscarProperties;
import oscar.oscarEncounter.oscarMeasurements.dao.MeasurementsDao;
import oscar.oscarEncounter.oscarMeasurements.model.Measurements;
import oscar.oscarLab.ca.all.pageUtil.LabUploadAction;
import oscar.oscarLab.ca.all.pageUtil.LabUploadForm;
import oscar.oscarLab.ca.all.util.Utilities;
import ca.uhn.hl7v2.model.v23.datatype.CE;
import ca.uhn.hl7v2.model.v23.group.ORU_R01_ORDER_OBSERVATION;
import ca.uhn.hl7v2.model.v23.group.ORU_R01_RESPONSE;
import ca.uhn.hl7v2.model.v23.message.ORU_R01;
import ca.uhn.hl7v2.model.v23.segment.MSH;
import ca.uhn.hl7v2.model.v23.segment.OBX;
import ca.uhn.hl7v2.model.v23.segment.PID;
import ca.uhn.hl7v2.parser.GenericParser;
import ca.uhn.hl7v2.parser.Parser;
import ca.uhn.hl7v2.validation.impl.NoValidation;

public class MeasurementHL7UploaderAction extends DispatchActionSupport {
    private static Logger logger = Logger.getLogger(MeasurementHL7UploaderAction.class);

    private static SimpleDateFormat sdf = new SimpleDateFormat(
            OscarProperties.getInstance().getProperty("oscar.measurements.hl7.datetime.format", "yyyyMMddHHmmss"));

    private MeasurementsDao measurementsDao;

    // settings to be set in spring config xml, if needed
    private String defaultProviderNo = OscarProperties.getInstance()
            .getProperty("oscar.measurements.hl7.defaultProviderNo", "999998");
    private String hl7UploadPassword = OscarProperties.getInstance().getProperty("oscar.measurements.hl7.password");

    public void setHl7UploadPassword(String uploadPassword) {
        this.hl7UploadPassword = uploadPassword;
    }

    public void setMeasurementsDao(MeasurementsDao measurementsDao) {
        this.measurementsDao = measurementsDao;
    }

    public void setDefaultProviderNo(String defaultProviderNo) {
        this.defaultProviderNo = defaultProviderNo;
    }

    public ActionForward upload(ActionMapping mapping, ActionForm form, HttpServletRequest request,
            HttpServletResponse response) {
        Date dateEntered = new Date();
        String hl7msg = null;

        try {
            boolean checkPassword = StringUtils.isNotBlank(hl7UploadPassword);

            // file is encrypted using RSA public keys if no password enforced
            hl7msg = checkPassword ? IOUtils.toString(((LabUploadForm) form).getImportFile().getInputStream())
                    : extractEncryptedMessage((LabUploadForm) form, request);

            if (checkPassword && hl7UploadPassword.length() < 16)
                throw new RuntimeException(
                        "Upload password length is too weak, please check oscar property file and make sure it's more than 16 letters.");

            Parser p = new GenericParser();
            p.setValidationContext(new NoValidation());
            ORU_R01 msg = (ORU_R01) p.parse(hl7msg);

            MSH msh = msg.getMSH();
            String msgType = msh.getMessageType().getMessageType().getValue() + "_"
                    + msh.getMessageType().getTriggerEvent().getValue();

            if (!"ORU_R01".equals(msgType))
                throw new RuntimeException("Message type is not ORU_R01 - " + msgType);

            if (checkPassword && !hl7UploadPassword.equals(msh.getSecurity().getValue()))
                throw new RuntimeException("Password in MSH is invalid.");

            String sender = msh.getSendingApplication().getNamespaceID().getValue();
            String receiver = msh.getReceivingApplication().getNamespaceID().getValue();

            String msgId = msh.getMessageControlID().getValue();
            logger.info("HL7 message [" + msgId + "] received from: " + sender + " to: " + receiver + " on "
                    + dateEntered);

            // TODO: handle multiple responses in one upload, right now only
            // assumes 1 per upload
            ORU_R01_RESPONSE resp = msg.getRESPONSE();

            PID patient = resp.getPATIENT().getPID();

            String hcn = patient.getPatientIDInternalID(0).getID().getValue();
            String hcnType = patient.getPatientIDInternalID(0).getAssigningAuthority().getNamespaceID().getValue();
            // get demographic no from hcn
            org.oscarehr.common.dao.DemographicDao demographicDao = (org.oscarehr.common.dao.DemographicDao) SpringUtils
                    .getBean("demographicDao");
            List<Demographic> demos = demographicDao.getActiveDemosByHealthCardNo(hcn, hcnType);
            if (demos == null || demos.size() == 0)
                throw new RuntimeException(
                        "There is no active patient with the supplied health card number: " + hcn + " " + hcnType);

            // try to get consult doctor's providerID
            String providerNo = resp.getPATIENT().getVISIT().getPV1().getConsultingDoctor(0).getIDNumber()
                    .getValue();
            if (providerNo == null)
                providerNo = defaultProviderNo;

            // now handle OBR
            ORU_R01_ORDER_OBSERVATION obr = resp.getORDER_OBSERVATION();

            CE univId = obr.getOBR().getUniversalServiceIdentifier();

            // now get all observation data
            int len = obr.getOBSERVATIONReps();
            for (int i = 0; i < len; i++) {
                logger.info("Processing OBX no." + i);
                OBX obx = obr.getOBSERVATION(i).getOBX();
                Date dateObserved = sdf.parse(obx.getDateTimeOfTheObservation().getTimeOfAnEvent().getValue());
                CE obvId = obx.getObservationIdentifier();
                // the 1st part of obvId is the unique short name
                String measurementType = obvId.getIdentifier().getValue();

                String unit = obx.getUnits().getIdentifier().getValue();
                if (unit == null) // oscar does not allow unit to be null
                    unit = "";
                String range = obx.getReferencesRange().getValue();
                if (range != null)
                    unit += " Range:" + range;

                String data = obx.getObservationValue(0).getData().toString();

                String abnormal = StringUtils.join(obx.getAbnormalFlags(), "|");
                if (StringUtils.isNotEmpty(abnormal))
                    abnormal = " Abnormal:" + abnormal;

                logger.info(
                        measurementType + " : " + data + " : " + unit + " : " + dateObserved + " : " + abnormal);

                // since oscar may have duplicate patient records, just add to
                // all of them.
                for (Demographic demo : demos) {
                    Integer demographicNo = demo.getDemographicNo();

                    // add to oscar measurements table
                    Measurements m = new Measurements();
                    m.setComments(abnormal + " by " + sender);
                    m.setDataField(data);
                    m.setDateEntered(dateEntered);
                    m.setDateObserved(dateObserved);
                    m.setDemographicNo(demographicNo);
                    m.setMeasuringInstruction(unit);
                    m.setProviderNo(providerNo);
                    m.setType(measurementType);

                    measurementsDao.addMeasurements(m);
                }
            }

        } catch (Exception e) {
            logger.error("Failed to parse HL7 ORU_R01 messages:\n" + hl7msg, e);
            response.setStatus(HttpStatus.SC_BAD_REQUEST);
            try {
                response.getWriter().println("Invalid HL7 ORU_R01 format/request: " + e.getMessage());
            } catch (IOException e1) {
                // TODO Auto-generated catch blockMiscUtils.getLogger().error("Error", e1);
            }
            return null;
        }

        response.setStatus(HttpStatus.SC_OK);
        return null;
    }

    /**
     * Extract encrypted data from http request. Assuming message file is attached as multipart request, and <LI>signature - signed by MD5WithRSA <LI>key - symetric key encrypted by client's private key <LI>service - the ID of client's public key in
     * oscar's 'publicKeys' table All keys are base64 encoded.
     *
     * @param form
     * @param request
     * @return
     */
    private String extractEncryptedMessage(LabUploadForm form, HttpServletRequest request) {
        FormFile importFile = form.getImportFile();

        String signature = request.getParameter("signature");
        String key = request.getParameter("key");
        String service = request.getParameter("service");

        ArrayList<Object> clientInfo = LabUploadAction.getClientInfo(service);
        PublicKey clientKey = (PublicKey) clientInfo.get(0);
        String type = (String) clientInfo.get(1);

        try {

            InputStream is = LabUploadAction.decryptMessage(importFile.getInputStream(), key, clientKey);
            String fileName = importFile.getFileName();
            String filePath = Utilities.saveFile(is, fileName);

            File file = new File(filePath);

            if (LabUploadAction.validateSignature(clientKey, signature, file)) {
                return FileUtils.readFileToString(file);
            } else
                throw new RuntimeException("Invalid signature, upload rejected.");

        } catch (Exception e) {
            throw new RuntimeException("Failed to decrypt upload file stream", e);

        } finally {
            try {
                importFile.getInputStream().close();
            } catch (Exception e) {
            }
        }

    }

}