org.openmrs.reporting.export.DataExportUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.openmrs.reporting.export.DataExportUtil.java

Source

/**
 * The contents of this file are subject to the OpenMRS Public License
 * Version 1.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://license.openmrs.org
 *
 * 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.
 *
 * Copyright (C) OpenMRS, LLC.  All Rights Reserved.
 */
package org.openmrs.reporting.export;

import java.io.File;
import java.io.PrintWriter;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.WeakHashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.log.CommonsLogLogChute;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.app.event.EventCartridge;
import org.apache.velocity.app.event.MethodExceptionEventHandler;
import org.openmrs.Cohort;
import org.openmrs.api.APIException;
import org.openmrs.api.context.Context;
import org.openmrs.report.EvaluationContext;
import org.openmrs.util.OpenmrsUtil;

/**
 * Utility methods for use by Data Exports
 * 
 * @deprecated see reportingcompatibility module
 */
@Deprecated
public class DataExportUtil {

    private static Map<String, Object> dataExportKeys = new WeakHashMap<String, Object>();

    /**
     * Allows a module or some other service to add things to the available keys in the velocity
     * context
     * 
     * @see #generateExport(DataExportReportObject, Cohort, DataExportFunctions, EvaluationContext)
     */
    public static void putDataExportKey(String key, Object obj) {
        dataExportKeys.put(key, obj);
    }

    /**
     * Remove the given key from the available data export keys If the key doesn't exist, this will
     * fail silently
     * 
     * @param key key to remove
     * @see #putDataExportKey(String, Object)
     * @see #generateExport(DataExportReportObject, Cohort, DataExportFunctions, EvaluationContext)
     */
    public static void removeDataExportKey(String key) {
        dataExportKeys.remove(key);
    }

    /**
     * Find the data export key previously added or null if not found
     * 
     * @param key
     * @return Object the Data Export Key with the key identifier. Returns null if not found
     * @see #putDataExportKey(String, Object)
     * @see #generateExport(DataExportReportObject, Cohort, DataExportFunctions, EvaluationContext)
     */
    public static Object getDataExportKey(String key) {
        return dataExportKeys.get(key);
    }

    /**
     * @param exports
     */
    public static void generateExports(List<DataExportReportObject> exports, EvaluationContext context) {

        Log log = LogFactory.getLog(DataExportUtil.class);

        for (DataExportReportObject dataExport : exports) {
            try {
                generateExport(dataExport, null, context);
            } catch (Exception e) {
                log.warn("Error while generating export: " + dataExport, e);
            }
        }

    }

    /**
     * Generates a data export file given a data export (columns) and patient set (rows).
     * 
     * @param dataExport
     * @param patientSet
     * @param separator
     * @throws Exception
     */
    public static void generateExport(DataExportReportObject dataExport, Cohort patientSet, String separator,
            EvaluationContext context) throws Exception {
        // Set up functions used in the report ( $!{fn:...} )
        DataExportFunctions functions = new DataExportFunctions();
        functions.setSeparator(separator);
        generateExport(dataExport, patientSet, functions, context);
    }

    /**
     * @param dataExport
     * @param patientSet
     * @throws Exception
     */
    public static void generateExport(DataExportReportObject dataExport, Cohort patientSet,
            EvaluationContext context) throws Exception {
        // Set up functions used in the report ( $!{fn:...} )
        DataExportFunctions functions = new DataExportFunctions();
        generateExport(dataExport, patientSet, functions, context);
    }

    /**
     * Auto generated method comment
     * 
     * @param dataExport
     * @param patientSet
     * @param functions
     * @param context
     * @throws Exception
     */
    public static void generateExport(DataExportReportObject dataExport, Cohort patientSet,
            DataExportFunctions functions, EvaluationContext context) throws Exception {

        // defining log file here to attempt to reduce memory consumption
        Log log = LogFactory.getLog(DataExportUtil.class);

        VelocityEngine velocityEngine = new VelocityEngine();

        velocityEngine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS,
                "org.apache.velocity.runtime.log.CommonsLogLogChute");
        velocityEngine.setProperty(CommonsLogLogChute.LOGCHUTE_COMMONS_LOG_NAME, "dataexport_velocity");

        try {
            velocityEngine.init();
        } catch (Exception e) {
            log.error("Error initializing Velocity engine", e);
        }

        File file = getGeneratedFile(dataExport);
        PrintWriter report = new PrintWriter(file);

        VelocityContext velocityContext = new VelocityContext();

        // Set up list of patients if one wasn't passed into this method
        if (patientSet == null) {
            patientSet = dataExport.generatePatientSet(context);
            functions.setAllPatients(dataExport.isAllPatients());
        }

        // add the error handler
        EventCartridge ec = new EventCartridge();
        ec.addEventHandler(new VelocityExceptionHandler());
        velocityContext.attachEventCartridge(ec);

        // Set up velocity utils
        Locale locale = Context.getLocale();
        velocityContext.put("locale", locale);
        velocityContext.put("fn", functions);

        /*
         * If we have any additional velocity objects that need to 
         * be added, do so here.
         */
        if (dataExportKeys != null && dataExportKeys.size() != 0) {
            for (Map.Entry<String, Object> entry : dataExportKeys.entrySet()) {
                velocityContext.put(entry.getKey(), entry.getValue());
            }
        }

        velocityContext.put("patientSet", patientSet);

        String template = dataExport.generateTemplate();

        // check if some deprecated columns are being used in this export
        // warning: hacky.
        if (template.contains("fn.getPatientAttr('Patient', 'tribe')")) {
            throw new APIException("Unable to generate export: " + dataExport.getName()
                    + " because it contains a reference to an outdated 'tribe' column.  You must install the 'Tribe Module' into OpenMRS to continue to reference tribes in OpenMRS.");
        }

        if (log.isDebugEnabled())
            log.debug("Template: " + template.substring(0, template.length() < 3500 ? template.length() : 3500)
                    + "...");

        try {
            velocityEngine.evaluate(velocityContext, report, DataExportUtil.class.getName(), template);
        } catch (Exception e) {
            log.error("Error evaluating data export " + dataExport.getReportObjectId(), e);
            log.error("Template: " + template.substring(0, template.length() < 3500 ? template.length() : 3500)
                    + "...");
            report.print("\n\nError: \n" + e.toString() + "\n Stacktrace: \n");
            e.printStackTrace(report);
        } finally {
            report.close();
            velocityContext.remove("fn");
            velocityContext.remove("patientSet");
            velocityContext = null;

            // reset the ParserPool to something else now?
            // using this to get to RuntimeInstance.init();
            velocityEngine.init();

            velocityEngine = null;

            patientSet = null;
            functions.clear();
            functions = null;
            template = null;
            dataExport = null;
            log.debug("Clearing hibernate session");
            Context.clearSession();

            // clear out the excess objects
            System.gc();
            System.gc();
        }

    }

    /**
     * Returns the path and name of the generated file
     * 
     * @param dataExport
     */
    public static File getGeneratedFile(DataExportReportObject dataExport) {
        File dir = new File(OpenmrsUtil.getApplicationDataDirectory(), "dataExports");
        dir.mkdirs();

        String filename = dataExport.getName().replace(" ", "_");
        filename += "_" + Context.getLocale().toString().toLowerCase();

        File file = new File(dir, filename);

        return file;
    }

    /**
     * Private class used for velocity error masking
     */
    public static class VelocityExceptionHandler implements MethodExceptionEventHandler {

        private Log log = LogFactory.getLog(this.getClass());

        /**
         * When a user-supplied method throws an exception, the MethodExceptionEventHandler is
         * invoked with the Class, method name and thrown Exception. The handler can either return a
         * valid Object to be used as the return value of the method call, or throw the passed-in or
         * new Exception, which will be wrapped and propogated to the user as a
         * MethodInvocationException
         * 
         * @see org.apache.velocity.app.event.MethodExceptionEventHandler#methodException(java.lang.Class,
         *      java.lang.String, java.lang.Exception)
         */
        @SuppressWarnings("unchecked")
        public Object methodException(Class claz, String method, Exception e) throws Exception {

            log.debug("Claz: " + claz.getName() + " method: " + method, e);

            // if formatting a date (and probably getting an "IllegalArguementException")
            if ("format".equals(method))
                return null;

            // keep the default behaviour
            throw e;
        }

    }

}