com.indivica.olis.Driver.java Source code

Java tutorial

Introduction

Here is the source code for com.indivica.olis.Driver.java

Source

/**
 * Copyright (c) 2008-2012 Indivica Inc.
 *
 * This software is made available under the terms of the
 * GNU General Public License, Version 2, 1991 (GPLv2).
 * License details are available via "indivica.ca/gplv2"
 * and "gnu.org/licenses/gpl-2.0.html".
 */

package com.indivica.olis;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.io.StringReader;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.SchemaFactory;

import org.apache.axis2.transport.http.HTTPConstants;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
import org.bouncycastle.util.encoders.Base64;
import org.oscarehr.common.dao.OscarLogDao;
import org.oscarehr.common.model.OscarLog;
import org.oscarehr.common.model.Provider;
import org.oscarehr.olis.OLISProtocolSocketFactory;
import org.oscarehr.util.LoggedInInfo;
import org.oscarehr.util.MiscUtils;
import org.oscarehr.util.SpringUtils;
import org.xml.sax.InputSource;

import oscar.OscarProperties;
import oscar.oscarMessenger.data.MsgProviderData;
import ca.ssha._2005.hial.ArrayOfError;
import ca.ssha._2005.hial.ArrayOfString;
import ca.ssha._2005.hial.Response;
import ca.ssha.www._2005.hial.OLISStub;
import ca.ssha.www._2005.hial.OLISStub.HIALRequest;
import ca.ssha.www._2005.hial.OLISStub.HIALRequestSignedRequest;
import ca.ssha.www._2005.hial.OLISStub.OLISRequest;
import ca.ssha.www._2005.hial.OLISStub.OLISRequestResponse;

import com.indivica.olis.queries.Query;

public class Driver {

    private static OscarLogDao logDao = (OscarLogDao) SpringUtils.getBean("oscarLogDao");

    public static String submitOLISQuery(HttpServletRequest request, Query query) {
        try {
            OLISMessage message = new OLISMessage(query);

            System.setProperty("javax.net.ssl.trustStore",
                    OscarProperties.getInstance().getProperty("olis_truststore").trim());
            System.setProperty("javax.net.ssl.trustStorePassword",
                    OscarProperties.getInstance().getProperty("olis_truststore_password").trim());

            OLISRequest olisRequest = new OLISRequest();
            olisRequest.setHIALRequest(new HIALRequest());
            String olisRequestURL = OscarProperties.getInstance().getProperty("olis_request_url",
                    "https://olis.ssha.ca/ssha.olis.webservices.ER7/OLIS.asmx");
            OLISStub olis = new OLISStub(olisRequestURL);
            olis._getServiceClient().getOptions().setProperty(HTTPConstants.CUSTOM_PROTOCOL_HANDLER,
                    new Protocol("https", (ProtocolSocketFactory) new OLISProtocolSocketFactory(), 443));

            olisRequest.getHIALRequest().setClientTransactionID(message.getTransactionId());
            olisRequest.getHIALRequest().setSignedRequest(new HIALRequestSignedRequest());

            String olisHL7String = message.getOlisHL7String().replaceAll("\n", "\r");
            String msgInXML = String.format(
                    "<Request xmlns=\"http://www.ssha.ca/2005/HIAL\"><Content><![CDATA[%s]]></Content></Request>",
                    olisHL7String);

            String signedRequest = null;

            if (OscarProperties.getInstance().getProperty("olis_returned_cert") != null) {
                signedRequest = Driver.signData2(msgInXML);
            } else {
                signedRequest = Driver.signData(msgInXML);
            }

            olisRequest.getHIALRequest().getSignedRequest().setSignedData(signedRequest);

            try {
                OscarLog logItem = new OscarLog();
                logItem.setAction("OLIS");
                logItem.setContent("query");
                logItem.setData(olisHL7String);

                LoggedInInfo loggedInInfo = LoggedInInfo.getLoggedInInfoFromSession(request);
                if (loggedInInfo.getLoggedInProvider() != null)
                    logItem.setProviderNo(loggedInInfo.getLoggedInProviderNo());

                logDao.persist(logItem);

            } catch (Exception e) {
                MiscUtils.getLogger().error("Couldn't write log message for OLIS query", e);
            }

            if (OscarProperties.getInstance().getProperty("olis_simulate", "no").equals("yes")) {
                String response = (String) request.getSession().getAttribute("olisResponseContent");
                request.setAttribute("olisResponseContent", response);
                request.getSession().setAttribute("olisResponseContent", null);
                return response;
            } else {
                OLISRequestResponse olisResponse = olis.oLISRequest(olisRequest);

                String signedData = olisResponse.getHIALResponse().getSignedResponse().getSignedData();
                String unsignedData = Driver.unsignData(signedData);
                //MiscUtils.getLogger().info(msgInXML);
                //MiscUtils.getLogger().info("---------------------------------");         
                //MiscUtils.getLogger().info(unsignedData);

                if (request != null) {
                    request.setAttribute("msgInXML", msgInXML);
                    request.setAttribute("signedRequest", signedRequest);
                    request.setAttribute("signedData", signedData);
                    request.setAttribute("unsignedResponse", unsignedData);
                }

                writeToFile(unsignedData);
                readResponseFromXML(request, unsignedData);

                return unsignedData;

            }
        } catch (Exception e) {
            MiscUtils.getLogger().error("Can't perform OLIS query due to exception.", e);
            if (request != null) {
                request.setAttribute("searchException", e);
            }

            LoggedInInfo loggedInInfo = LoggedInInfo.getLoggedInInfoFromSession(request);
            notifyOlisError(loggedInInfo.getLoggedInProvider(), e.getMessage());
            return "";
        }
    }

    public static void readResponseFromXML(HttpServletRequest request, String olisResponse) {

        olisResponse = olisResponse.replaceAll("<Content", "<Content xmlns=\"\" ");
        olisResponse = olisResponse.replaceAll("<Errors", "<Errors xmlns=\"\" ");

        try {
            DocumentBuilderFactory.newInstance().newDocumentBuilder();
            SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");

            Source schemaFile = new StreamSource(
                    new File(OscarProperties.getInstance().getProperty("olis_response_schema")));
            factory.newSchema(schemaFile);

            JAXBContext jc = JAXBContext.newInstance("ca.ssha._2005.hial");
            Unmarshaller u = jc.createUnmarshaller();
            @SuppressWarnings("unchecked")
            Response root = ((JAXBElement<Response>) u.unmarshal(new InputSource(new StringReader(olisResponse))))
                    .getValue();

            if (root.getErrors() != null) {
                List<String> errorStringList = new LinkedList<String>();

                // Read all the errors
                ArrayOfError errors = root.getErrors();
                List<ca.ssha._2005.hial.Error> errorList = errors.getError();

                for (ca.ssha._2005.hial.Error error : errorList) {
                    String errorString = "";
                    errorString += "ERROR " + error.getNumber() + " (" + error.getSeverity() + ") : "
                            + error.getMessage();
                    MiscUtils.getLogger().debug(errorString);

                    ArrayOfString details = error.getDetails();
                    if (details != null) {
                        List<String> detailList = details.getString();
                        for (String detail : detailList) {
                            errorString += "\n" + detail;
                        }
                    }

                    errorStringList.add(errorString);
                }
                if (request != null)
                    request.setAttribute("errors", errorStringList);
            } else if (root.getContent() != null) {
                if (request != null)
                    request.setAttribute("olisResponseContent", root.getContent());
            }
        } catch (Exception e) {
            MiscUtils.getLogger().error("Couldn't read XML from OLIS response.", e);

            LoggedInInfo loggedInInfo = LoggedInInfo.getLoggedInInfoFromSession(request);
            notifyOlisError(loggedInInfo.getLoggedInProvider(), "Couldn't read XML from OLIS response." + "\n" + e);
        }
    }

    public static String unsignData(String data) {

        byte[] dataBytes = Base64.decode(data);

        try {

            CMSSignedData s = new CMSSignedData(dataBytes);
            Store certs = s.getCertificates();
            SignerInformationStore signers = s.getSignerInfos();
            @SuppressWarnings("unchecked")
            Collection<SignerInformation> c = signers.getSigners();
            Iterator<SignerInformation> it = c.iterator();
            while (it.hasNext()) {
                X509CertificateHolder cert = null;
                SignerInformation signer = it.next();
                Collection certCollection = certs.getMatches(signer.getSID());
                @SuppressWarnings("unchecked")
                Iterator<X509CertificateHolder> certIt = certCollection.iterator();
                cert = certIt.next();

                if (!signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert)))
                    throw new Exception("Doesn't verify");
            }

            CMSProcessableByteArray cpb = (CMSProcessableByteArray) s.getSignedContent();
            byte[] signedContent = (byte[]) cpb.getContent();
            String content = new String(signedContent);
            return content;
        } catch (Exception e) {
            MiscUtils.getLogger().error("error", e);
        }
        return null;

    }

    //Method uses a jks and a returned cert separately instead of needing to 
    //import the cert into PKCS12 file.
    public static String signData2(String data) {
        X509Certificate cert = null;
        PrivateKey priv = null;
        KeyStore keystore = null;
        String pwd = OscarProperties.getInstance().getProperty("olis_ssl_keystore_password", "changeit");
        String result = null;
        try {
            Security.addProvider(new BouncyCastleProvider());

            keystore = KeyStore.getInstance("JKS");
            // Load the keystore
            keystore.load(new FileInputStream(OscarProperties.getInstance().getProperty("olis_keystore")),
                    pwd.toCharArray());

            //Enumeration e = keystore.aliases();
            String name = "olis";

            // Get the private key and the certificate
            priv = (PrivateKey) keystore.getKey(name, pwd.toCharArray());

            FileInputStream is = new FileInputStream(
                    OscarProperties.getInstance().getProperty("olis_returned_cert"));
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            cert = (X509Certificate) cf.generateCertificate(is);

            // I'm not sure if this is necessary

            ArrayList<Certificate> certList = new ArrayList<Certificate>();
            certList.add(cert);

            Store certs = new JcaCertStore(certList);

            // Encrypt data
            CMSSignedDataGenerator sgen = new CMSSignedDataGenerator();

            // What digest algorithm i must use? SHA1? MD5? RSA?...
            ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(priv);
            sgen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(
                    new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()).build(sha1Signer, cert));

            // I'm not sure this is necessary
            sgen.addCertificates(certs);

            // I think that the 2nd parameter need to be false (detached form)
            CMSSignedData csd = sgen.generate(new CMSProcessableByteArray(data.getBytes()), true);

            byte[] signedData = csd.getEncoded();
            byte[] signedDataB64 = Base64.encode(signedData);

            result = new String(signedDataB64);

        } catch (Exception e) {
            MiscUtils.getLogger().error("Can't sign HL7 message for OLIS", e);
        }
        return result;
    }

    public static String signData(String data) {
        X509Certificate cert = null;
        PrivateKey priv = null;
        KeyStore keystore = null;
        String pwd = "Olis2011";
        String result = null;
        try {
            Security.addProvider(new BouncyCastleProvider());

            keystore = KeyStore.getInstance("PKCS12", "SunJSSE");
            // Load the keystore
            keystore.load(new FileInputStream(OscarProperties.getInstance().getProperty("olis_keystore")),
                    pwd.toCharArray());

            Enumeration e = keystore.aliases();
            String name = "";

            if (e != null) {
                while (e.hasMoreElements()) {
                    String n = (String) e.nextElement();
                    if (keystore.isKeyEntry(n)) {
                        name = n;
                    }
                }
            }

            // Get the private key and the certificate
            priv = (PrivateKey) keystore.getKey(name, pwd.toCharArray());
            cert = (X509Certificate) keystore.getCertificate(name);

            // I'm not sure if this is necessary

            ArrayList<Certificate> certList = new ArrayList<Certificate>();
            certList.add(cert);

            Store certs = new JcaCertStore(certList);

            // Encrypt data
            CMSSignedDataGenerator sgen = new CMSSignedDataGenerator();

            // What digest algorithm i must use? SHA1? MD5? RSA?...
            ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(priv);
            sgen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(
                    new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()).build(sha1Signer, cert));

            // I'm not sure this is necessary
            sgen.addCertificates(certs);

            // I think that the 2nd parameter need to be false (detached form)
            CMSSignedData csd = sgen.generate(new CMSProcessableByteArray(data.getBytes()), true);

            byte[] signedData = csd.getEncoded();
            byte[] signedDataB64 = Base64.encode(signedData);

            result = new String(signedDataB64);

        } catch (Exception e) {
            MiscUtils.getLogger().error("Can't sign HL7 message for OLIS", e);
        }
        return result;
    }

    private static void notifyOlisError(Provider provider, String errorMsg) {
        HashSet<String> sendToProviderList = new HashSet<String>();

        String providerNoTemp = "999998";
        sendToProviderList.add(providerNoTemp);

        if (provider != null) {
            // manual prompts always send to admin
            sendToProviderList.add(providerNoTemp);

            providerNoTemp = provider.getProviderNo();
            sendToProviderList.add(providerNoTemp);
        }

        // no one wants to hear about the problem
        if (sendToProviderList.size() == 0)
            return;

        String message = "OSCAR attempted to perform a fetch of OLIS data at " + new Date()
                + " but there was an error during the task.\n\nSee below for the error message:\n" + errorMsg;

        oscar.oscarMessenger.data.MsgMessageData messageData = new oscar.oscarMessenger.data.MsgMessageData();

        ArrayList<MsgProviderData> sendToProviderListData = new ArrayList<MsgProviderData>();
        for (String providerNo : sendToProviderList) {
            MsgProviderData mpd = new MsgProviderData();
            mpd.providerNo = providerNo;
            mpd.locationId = "145";
            sendToProviderListData.add(mpd);
        }

        String sentToString = messageData.createSentToString(sendToProviderListData);
        messageData.sendMessage2(message, "OLIS Retrieval Error", "System", sentToString, "-1",
                sendToProviderListData, null, null);
    }

    static void writeToFile(String data) {
        try {
            File tempFile = new File(System.getProperty("java.io.tmpdir") + (Math.random() * 100) + ".xml");
            PrintWriter pw = new PrintWriter(new FileWriter(tempFile));
            pw.println(data);
            pw.flush();
            pw.close();
        } catch (Exception e) {
            MiscUtils.getLogger().error("Error", e);
        }
    }
}