us.mn.state.health.lims.common.externalLinks.ExternalPatientSearch.java Source code

Java tutorial

Introduction

Here is the source code for us.mn.state.health.lims.common.externalLinks.ExternalPatientSearch.java

Source

/**
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 * 
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 * 
 * The Original Code is OpenELIS code.
 * 
 * Copyright (C) The Minnesota Department of Health.  All Rights Reserved.
 *
 * Contributor(s): CIRG, University of Washington, Seattle WA.
 */
package us.mn.state.health.lims.common.externalLinks;

import java.io.IOException;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.io.IOUtils;
import org.apache.commons.validator.GenericValidator;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.dom4j.DocumentException;

import us.mn.state.health.lims.common.log.LogEvent;
import us.mn.state.health.lims.common.provider.query.PatientDemographicsSearchResults;

public class ExternalPatientSearch implements Runnable {

    private static final String GET_PARAM_PWD = "pwd";
    private static final String GET_PARAM_NAME = "name";
    private static final String GET_PARAM_NATIONAL_ID = "nationalId";
    private static final String GET_PARAM_ST = "ST";
    private static final String GET_PARAM_SUBJECT = "subjectNumber";
    private static final String GET_PARAM_LAST = "last";
    private static final String GET_PARAM_FIRST = "first";
    private static final String GET_PARAM_GUID = "guid";

    public static final String MALFORMED_REPLY = "Malformed reply";
    public static final String URI_BUILD_FAILURE = "Failed to build URI";

    private boolean finished = false;

    private String firstName;
    private String lastName;
    private String STNumber;
    private String subjectNumber;
    private String nationalId;
    private String guid;
    private String connectionString;
    private String connectionName;
    private String connectionPassword;
    private int timeout = 0;

    protected String resultXML;
    protected List<PatientDemographicsSearchResults> searchResults;
    protected List<String> errors;
    protected int returnStatus = HttpStatus.SC_CREATED;

    synchronized public void setConnectionCredentials(String connectionString, String name, String password,
            int timeout_Mil) {
        if (finished) {
            throw new IllegalStateException(
                    "ServiceCredentials set after ExternalPatientSearch thread was started");
        }

        this.connectionString = connectionString;
        connectionName = name;
        connectionPassword = password;
        timeout = timeout_Mil;
    }

    synchronized public void setSearchCriteria(String lastName, String firstName, String STNumber,
            String subjectNumber, String nationalID, String guid) throws IllegalStateException {

        if (finished) {
            throw new IllegalStateException("Search criteria set after ExternalPatientSearch thread was started");
        }

        this.lastName = lastName;
        this.firstName = firstName;
        this.STNumber = STNumber;
        this.subjectNumber = subjectNumber;
        this.nationalId = nationalID;
        this.guid = guid;
    }

    synchronized public List<PatientDemographicsSearchResults> getSearchResults() {

        if (!finished) {
            throw new IllegalStateException("Results requested before ExternalPatientSearch thread was finished");
        }

        if (searchResults == null) {
            searchResults = new ArrayList<PatientDemographicsSearchResults>();

            convertXMLToResults();
        }

        return searchResults;
    }

    public int getSearchResultStatus() {
        if (!finished) {
            throw new IllegalStateException(
                    "Result status requested ExternalPatientSearch before search was finished");
        }

        return returnStatus;
    }

    public void run() {
        try {
            synchronized (this) {
                if (noSearchTerms()) {
                    return;
                }

                if (connectionCredentialsIncomplete()) {
                    throw new IllegalStateException("Search requested before connection credentials set.");
                }
                errors = new ArrayList<String>();

                doSearch();
            }
        } finally {
            finished = true;
        }
    }

    private boolean connectionCredentialsIncomplete() {
        return GenericValidator.isBlankOrNull(connectionString) || GenericValidator.isBlankOrNull(connectionName)
                || GenericValidator.isBlankOrNull(connectionPassword);
    }

    private boolean noSearchTerms() {
        return GenericValidator.isBlankOrNull(firstName) && GenericValidator.isBlankOrNull(lastName)
                && GenericValidator.isBlankOrNull(nationalId) && GenericValidator.isBlankOrNull(STNumber);
    }

    // protected for unit testing called from synchronized block
    protected void doSearch() {

        HttpClient httpclient = new DefaultHttpClient();
        setTimeout(httpclient);

        HttpGet httpget = new HttpGet(connectionString);
        URI getUri = buildConnectionString(httpget.getURI());
        httpget.setURI(getUri);

        try {
            // Ignore hostname mismatches and allow trust of self-signed certs
            SSLSocketFactory sslsf = new SSLSocketFactory(new TrustSelfSignedStrategy(),
                    SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            Scheme https = new Scheme("https", 443, sslsf);
            ClientConnectionManager ccm = httpclient.getConnectionManager();
            ccm.getSchemeRegistry().register(https);

            HttpResponse getResponse = httpclient.execute(httpget);
            returnStatus = getResponse.getStatusLine().getStatusCode();
            setPossibleErrors();
            setResults(IOUtils.toString(getResponse.getEntity().getContent(), "UTF-8"));
        } catch (SocketTimeoutException e) {
            errors.add("Response from patient information server took too long.");
            LogEvent.logError("ExternalPatientSearch", "doSearch()", e.toString());
            // System.out.println("Tinny time out" + e);
        } catch (ConnectException e) {
            errors.add("Unable to connect to patient information form service. Service may not be running");
            LogEvent.logError("ExternalPatientSearch", "doSearch()", e.toString());
            // System.out.println("you no talks? " + e);
        } catch (IOException e) {
            errors.add("IO error trying to read input stream.");
            LogEvent.logError("ExternalPatientSearch", "doSearch()", e.toString());
            // System.out.println("all else failed " + e);
        } catch (KeyManagementException e) {
            errors.add("Key management error trying to connect to external search service.");
            LogEvent.logError("ExternalPatientSearch", "doSearch()", e.toString());
        } catch (UnrecoverableKeyException e) {
            errors.add("Unrecoverable key error trying to connect to external search service.");
            LogEvent.logError("ExternalPatientSearch", "doSearch()", e.toString());
        } catch (NoSuchAlgorithmException e) {
            errors.add("No such encyrption algorithm error trying to connect to external search service.");
            LogEvent.logError("ExternalPatientSearch", "doSearch()", e.toString());
        } catch (KeyStoreException e) {
            errors.add("Keystore error trying to connect to external search service.");
            LogEvent.logError("ExternalPatientSearch", "doSearch()", e.toString());
        } catch (RuntimeException e) {
            errors.add("Runtime error trying to retrieve patient information.");
            LogEvent.logError("ExternalPatientSearch", "doSearch()", e.toString());
            httpget.abort();
            throw e;
        } finally {
            httpclient.getConnectionManager().shutdown();
        }
    }

    private void convertXMLToResults() {
        if (!GenericValidator.isBlankOrNull(resultXML)) {

            ExternalPatientSearchResultsXMLConverter converter = new ExternalPatientSearchResultsXMLConverter();

            try {
                searchResults = converter.convertXMLToSearchResults(resultXML);
            } catch (DocumentException e) {
                errors.add(MALFORMED_REPLY);
            }
        }
    }

    protected void setResults(String resultsAsXml) {
        resultXML = resultsAsXml;
    }

    private void setPossibleErrors() {
        switch (returnStatus) {
        case HttpStatus.SC_UNAUTHORIZED: {
            errors.add("Access denied to patient information service.");
            break;
        }
        case HttpStatus.SC_INTERNAL_SERVER_ERROR: {
            errors.add("Internal error on patient information service.");
            break;
        }
        case HttpStatus.SC_OK: {
            break; //NO-OP
        }
        default: {
            errors.add("Unknown error trying to connect to patient information service. Resturn status was "
                    + returnStatus);
        }
        }

    }

    private void setTimeout(HttpClient httpclient) {
        // this one causes a timeout if a connection is established but there is 
        // no response within <timeout> seconds
        httpclient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, timeout);

        // this one causes a timeout if no connection is established within 10 seconds
        httpclient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, timeout);
    }

    private URI buildConnectionString(URI uriStart) {

        URI uriFinal = null;
        try {
            uriFinal = new URIBuilder(uriStart).addParameter(GET_PARAM_FIRST, firstName)
                    .addParameter(GET_PARAM_LAST, lastName).addParameter(GET_PARAM_ST, STNumber)
                    .addParameter(GET_PARAM_SUBJECT, subjectNumber).addParameter(GET_PARAM_NATIONAL_ID, nationalId)
                    .addParameter(GET_PARAM_GUID, guid).addParameter(GET_PARAM_NAME, connectionName)
                    .addParameter(GET_PARAM_PWD, connectionPassword).build();
        } catch (URISyntaxException e) {
            errors.add(URI_BUILD_FAILURE);
        }

        return uriFinal;
    }
}