org.oscarehr.common.service.E2ESchedulerJob.java Source code

Java tutorial

Introduction

Here is the source code for org.oscarehr.common.service.E2ESchedulerJob.java

Source

/**
 * Copyright (c) 2013. Department of Family Practice, University of British Columbia. 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.
 *
 * This software was written for the
 * Department of Family Practice
 * Faculty of Medicine
 * University of British Columbia
 * Vancouver, Canada
 */
package org.oscarehr.common.service;

import java.net.NoRouteToHostException;
import java.util.Calendar;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.TimerTask;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.HttpHostConnectException;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.ByteArrayBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.log4j.Logger;
import org.oscarehr.common.dao.DemographicDao;
import org.oscarehr.common.dao.OscarLogDao;
import org.oscarehr.exports.e2e.E2EPatientExport;
import org.oscarehr.exports.e2e.E2EVelocityTemplate;
import org.oscarehr.util.DbConnectionFilter;
import org.oscarehr.util.MiscUtils;
import org.oscarehr.util.SpringUtils;

import oscar.OscarProperties;
import oscar.util.StringUtils;

/**
 * An E2E scheduler object for periodically exporting all available patient summaries via HTTP POST
 * This object extends the JDK TimerTask, but applicationContextE2E.xml uses Quartz scheduling instead
 * 
 * @author Marc Dumontier, Jeremy Ho
 */
public class E2ESchedulerJob extends TimerTask {
    private static final Logger logger = MiscUtils.getLogger();
    private static final String e2eUrl = OscarProperties.getInstance().getProperty("E2E_URL");
    private static final String e2eDiff = OscarProperties.getInstance().getProperty("E2E_DIFF");
    private static final String e2eDiffDays = OscarProperties.getInstance().getProperty("E2E_DIFF_DAYS");
    private static final boolean diffMode = e2eDiff != null && e2eDiff.toLowerCase().equals("on");

    @Override
    public void run() {
        DemographicDao demographicDao = SpringUtils.getBean(DemographicDao.class);
        OscarLogDao oscarLogDao = SpringUtils.getBean(OscarLogDao.class);
        StringBuilder sb = new StringBuilder(255);
        int success = 0;
        int failure = 0;
        int skipped = 0;
        int diffDays = 14;
        List<Integer> ids = null;

        try {
            // Gather demographic numbers for specified mode of operation
            if (diffMode) {
                if (e2eDiffDays != null && StringUtils.isNumeric(e2eDiffDays)) {
                    diffDays = Integer.parseInt(e2eDiffDays);
                }

                Calendar cal = GregorianCalendar.getInstance();
                cal.add(Calendar.DAY_OF_YEAR, -diffDays);
                ids = oscarLogDao.getDemographicIdsOpenedSinceTime(cal.getTime());
            } else {
                ids = demographicDao.getActiveDemographicIds();
            }
            if (ids != null)
                Collections.sort(ids);

            // Log Start Header
            StringBuilder sbStart = reuseStringBuilder(sb);
            sbStart.append("Starting E2E export job\nE2E Target URL: ").append(e2eUrl);
            if (diffMode) {
                sbStart.append("\nExport Mode: Differential - Days: ").append(diffDays);
            } else {
                sbStart.append("\nExport Mode: Full");
            }
            logger.info(sbStart.toString());
            StringBuilder sbStartRec = reuseStringBuilder(sb);
            sbStartRec.append(ids.size()).append(" records pending");
            if (ids.size() > 0) {
                sbStartRec.append("\nRange: ").append(ids.get(0)).append(" - ").append(ids.get(ids.size() - 1));
                sbStartRec.append(", Median: ").append(ids.get((ids.size() - 1) / 2));
            }
            logger.info(sbStartRec.toString());

            long startJob = System.currentTimeMillis();
            long endJob = startJob;

            for (Integer id : ids) {
                // Select Template
                E2EVelocityTemplate t = new E2EVelocityTemplate();

                // Create and load Patient data
                long startLoad = System.currentTimeMillis();
                E2EPatientExport patient = new E2EPatientExport();
                patient.setExAllTrue();
                long endLoad = startLoad;

                long startTemplate = 0;
                long endTemplate = startTemplate;
                // Load patient data and merge to template
                String output = "";
                if (patient.loadPatient(id.toString())) {
                    endLoad = System.currentTimeMillis();
                    if (patient.isActive()) {
                        startTemplate = System.currentTimeMillis();
                        output = t.export(patient);
                        endTemplate = System.currentTimeMillis();
                    } else {
                        logger.info("[Demo: ".concat(id.toString()).concat("] Not active - skipped"));
                        skipped++;
                        continue;
                    }
                } else {
                    endLoad = System.currentTimeMillis();
                    logger.error("[Demo: ".concat(id.toString()).concat("] Failed to load"));
                    failure++;
                    continue;
                }

                long startPost = System.currentTimeMillis();
                long endPost = startPost;

                // Attempt to perform HTTP POST request
                try {
                    HttpClient httpclient = new DefaultHttpClient();
                    HttpPost httpPost = new HttpPost(e2eUrl);

                    // Assemble Multi-part Request
                    StringBuilder sbFile = reuseStringBuilder(sb);
                    sbFile.append("output_").append(id).append(".xml");
                    ByteArrayBody body = new ByteArrayBody(output.getBytes(), "text/xml", sbFile.toString());
                    MultipartEntity reqEntity = new MultipartEntity();
                    reqEntity.addPart("content", body);
                    httpPost.setEntity(reqEntity);

                    // Send HTTP POST request
                    HttpResponse response = httpclient.execute(httpPost);
                    if (response != null && response.getStatusLine().getStatusCode() == 201) {
                        success++;
                    } else {
                        logger.warn(response.getStatusLine());
                        failure++;
                    }
                } catch (HttpHostConnectException e) {
                    logger.error("Connection to ".concat(e2eUrl).concat(" refused"));
                    failure++;
                } catch (NoRouteToHostException e) {
                    logger.error("Can't resolve route to ".concat(e2eUrl));
                    failure++;
                } catch (Exception e) {
                    logger.error("Error", e);
                    failure++;
                } finally {
                    endPost = System.currentTimeMillis();
                }

                // Log Record completion + benchmarks
                StringBuilder sbTimer = reuseStringBuilder(sb);
                sbTimer.append("[Demo: ").append(id);
                sbTimer.append("] L:").append((endLoad - startLoad) / 1000.0);
                sbTimer.append(" T:").append((endTemplate - startTemplate) / 1000.0);
                sbTimer.append(" P:").append((endPost - startPost) / 1000.0);
                logger.info(sbTimer.toString());
            }

            endJob = System.currentTimeMillis();
            logger.info("Done E2E export job (" + convertTime(endJob - startJob) + ")");
        } catch (Throwable e) {
            logger.error("Error", e);
            logger.info("E2E export job aborted");
        } finally {
            // Log final record counts
            int unaccounted = ids.size() - success - failure - skipped;
            sb = reuseStringBuilder(sb);
            sb.append(success).append(" records processed");
            if (failure > 0)
                sb.append("\n").append(failure).append(" records failed");
            if (skipped > 0)
                sb.append("\n").append(skipped).append(" records skipped");
            if (unaccounted > 0)
                sb.append("\n").append(unaccounted).append(" records unaccounted");
            logger.info(sb.toString());
            DbConnectionFilter.releaseAllThreadDbResources();
        }
    }

    /**
     * Helper function for reusing a StringBuilder object for efficiency
     * Blanks out existing sb contents and returns sb
     * 
     * @param sb
     */
    private StringBuilder reuseStringBuilder(final StringBuilder sb) {
        sb.delete(0, sb.length());
        return sb;
    }

    /**
     * Helper function to display time in a nicer readable format
     * 
     * @param time
     * @return string
     */
    public String convertTime(long time) {
        Long ms = time;
        Long seconds = time / 1000L;
        Long minutes = time / 60000L;
        Long hours = time / 3600000L;
        return String.format("%02d:%02d:%02d.%03d", hours % 24, minutes % 60, seconds % 60, ms % 1000);
    }
}