/** * 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; } } }