org.openbravo.client.application.report.BaseReportActionHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.openbravo.client.application.report.BaseReportActionHandler.java

Source

/*
 *************************************************************************
 * The contents of this file are subject to the Openbravo  Public  License
 * Version  1.1  (the  "License"),  being   the  Mozilla   Public  License
 * Version 1.1  with a permitted attribution clause; you may not  use this
 * file except in compliance with the License. You  may  obtain  a copy of
 * the License at http://www.openbravo.com/legal/license.html 
 * 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 Openbravo ERP. 
 * The Initial Developer of the Original Code is Openbravo SLU 
 * All portions are Copyright (C) 2014-2015 Openbravo SLU 
 * All Rights Reserved. 
 * Contributor(s):  ______________________________________.
 ************************************************************************
 */

package org.openbravo.client.application.report;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import javax.mail.internet.MimeUtility;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.openbravo.base.exception.OBException;
import org.openbravo.base.model.Entity;
import org.openbravo.base.model.ModelProvider;
import org.openbravo.base.model.domaintype.BigDecimalDomainType;
import org.openbravo.base.model.domaintype.BooleanDomainType;
import org.openbravo.base.model.domaintype.DateDomainType;
import org.openbravo.base.model.domaintype.DomainType;
import org.openbravo.base.model.domaintype.ForeignKeyDomainType;
import org.openbravo.base.model.domaintype.LongDomainType;
import org.openbravo.base.model.domaintype.StringDomainType;
import org.openbravo.base.model.domaintype.StringEnumerateDomainType;
import org.openbravo.base.session.OBPropertiesProvider;
import org.openbravo.base.structure.BaseOBObject;
import org.openbravo.client.application.ApplicationConstants;
import org.openbravo.client.application.Parameter;
import org.openbravo.client.application.ReportDefinition;
import org.openbravo.client.application.process.BaseProcessActionHandler;
import org.openbravo.client.application.report.ReportingUtils.ExportType;
import org.openbravo.client.kernel.KernelConstants;
import org.openbravo.client.kernel.RequestContext;
import org.openbravo.client.kernel.reference.UIDefinition;
import org.openbravo.client.kernel.reference.UIDefinitionController;
import org.openbravo.dal.core.DalContextListener;
import org.openbravo.dal.service.OBDal;
import org.openbravo.erpCommon.utility.OBMessageUtils;
import org.openbravo.userinterface.selector.reference.FKMultiSelectorUIDefinition;
import org.openbravo.utils.FileUtility;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Action Handler used as base for jasper reports generated from process defition. This handler can
 * be extended to customize its behavior.
 * 
 */
public class BaseReportActionHandler extends BaseProcessActionHandler {
    private static final Logger log = LoggerFactory.getLogger(BaseReportActionHandler.class);
    private static final String JASPER_PARAM_PROCESS = "jasper_process";

    /**
     * execute() method overridden to add the logic to download the report file stored in the
     * temporary folder.
     */
    @Override
    public void execute() {
        final HttpServletRequest request = RequestContext.get().getRequest();
        String mode = request.getParameter("mode");
        if (mode == null) {
            mode = "Default";
        }
        if ("DOWNLOAD".equals(mode)) {
            final Map<String, Object> parameterMap = new HashMap<String, Object>();
            for (Enumeration<?> keys = request.getParameterNames(); keys.hasMoreElements();) {
                final String key = (String) keys.nextElement();
                if (request.getParameterValues(key) != null && request.getParameterValues(key).length > 1) {
                    parameterMap.put(key, request.getParameterValues(key));
                } else {
                    parameterMap.put(key, request.getParameter(key));
                }
            }
            // also add the Http Stuff
            parameterMap.put(KernelConstants.HTTP_SESSION, request.getSession(false));
            parameterMap.put(KernelConstants.HTTP_REQUEST, request);
            try {
                doDownload(request, parameterMap);
            } catch (Exception e) {
                // Error downloading file
                log.error("Error downloading the file: " + e.getMessage(), e);
            }
            return;
        }
        super.execute();
    }

    @Override
    protected JSONObject doExecute(Map<String, Object> parameters, String content) {

        JSONObject result = new JSONObject();
        try {
            result.put("retryExecution", true);
            result.put("showResultsInProcessView", true);

            final JSONObject jsonContent = new JSONObject(content);
            final String action = jsonContent.getString(ApplicationConstants.BUTTON_VALUE);
            doGenerateReport(result, parameters, jsonContent, action);

            return result;
        } catch (OBException e) {
            log.error("Error generating report id: {}", parameters.get("reportId"), e);
            JSONObject msg = new JSONObject();
            try {
                msg.put("severity", "error");
                msg.put("text", OBMessageUtils.translateError(e.getMessage()).getMessage());
                result.put("message", msg);
            } catch (JSONException ignore) {
            }
            return result;
        } catch (Exception e) {
            log.error("Error generating report id: {}", parameters.get("reportId"), e);
            JSONObject msg = new JSONObject();
            try {
                msg.put("severity", "error");
                msg.put("text", OBMessageUtils.translateError(e.getMessage()).getMessage());
                result.put("message", msg);
            } catch (JSONException ignore) {
            }
            return result;
        }
    }

    /**
     * Downloads the file with the report result. The file is stored in a temporary folder with a
     * generated name. It is renamed and download as an attachment of the response. Once it is
     * finished the file is removed from the server.
     */
    private void doDownload(HttpServletRequest request, Map<String, Object> parameters) throws IOException {
        final String strFileName = (String) parameters.get("fileName");
        final String tmpFileName = (String) parameters.get("tmpfileName");
        ExportType expType = null;

        if (strFileName.endsWith("." + ExportType.PDF.getExtension())) {
            expType = ExportType.PDF;
        } else if (strFileName.endsWith("." + ExportType.XLS.getExtension())) {
            expType = ExportType.XLS;
        } else {
            throw new IllegalArgumentException(
                    "Trying to download report file with unsupported type " + strFileName);
        }

        if (!expType.isValidTemporaryFileName(tmpFileName)) {
            throw new IllegalArgumentException("Trying to download report with invalid name " + strFileName);
        }

        final String tmpDirectory = ReportingUtils.getTempFolder();
        final File file = new File(tmpDirectory, tmpFileName);
        FileUtility fileUtil = new FileUtility(tmpDirectory, tmpFileName, false, true);
        try {
            final HttpServletResponse response = RequestContext.get().getResponse();

            response.setHeader("Content-Type", expType.getContentType());
            response.setContentType(expType.getContentType());
            response.setCharacterEncoding("UTF-8");
            // TODO: Compatibility code with IE8. To be reviewed when its support is stopped.
            // see issue #29109
            String userAgent = request.getHeader("user-agent");
            if (userAgent.contains("MSIE")) {
                response.setHeader("Content-Disposition",
                        "attachment; filename=\"" + URLEncoder.encode(strFileName, "utf-8") + "\"");
            } else {
                response.setHeader("Content-Disposition",
                        "attachment; filename=\"" + MimeUtility.encodeWord(strFileName, "utf-8", "Q") + "\"");
            }

            fileUtil.dumpFile(response.getOutputStream());
            response.getOutputStream().flush();
            response.getOutputStream().close();
        } finally {
            if (file.exists()) {
                file.delete();
            }
        }
    }

    /**
     * Manages the report generation. It sets the proper response actions to download the generated
     * file.
     * 
     * @param result
     *          JSONObject with the response that is returned to the client.
     * @param parameters
     *          Map including the parameters of the call.
     * @param jsonContent
     *          JSONObject with the values set in the filter parameters.
     * @param action
     *          String with the output type of the report.
     * @throws OBException
     *           Exception thrown when a validation fails.
     */
    private void doGenerateReport(JSONObject result, Map<String, Object> parameters, JSONObject jsonContent,
            String action) throws JSONException, OBException {
        JSONObject params = jsonContent.getJSONObject("_params");
        final ReportDefinition report = OBDal.getInstance().get(ReportDefinition.class, parameters.get("reportId"));

        doValidations(report, parameters, jsonContent);
        final ExportType expType = ExportType.getExportType(action);

        String strFileName = getPDFFileName(report, parameters, expType);
        String strTmpFileName = UUID.randomUUID().toString() + "." + expType.getExtension();
        String strJRPath = "";
        switch (expType) {
        case XLS:
            strJRPath = report.getXLSTemplate();
            if (StringUtils.isNotEmpty(strJRPath) || !report.isUsePDFAsXLSTemplate()) {
                break;
            }
        case PDF:
            strJRPath = report.getPDFTemplate();
            break;
        default:
            throw new OBException(OBMessageUtils.getI18NMessage("OBUIAPP_UnsupportedAction",
                    new String[] { expType.getExtension() }));
        }
        if (StringUtils.isEmpty(strJRPath)) {
            throw new OBException(OBMessageUtils.messageBD("OBUIAPP_NoJRTemplateFound"));
        }

        final String jrTemplatePath = DalContextListener.getServletContext().getRealPath(strJRPath);
        HashMap<String, Object> jrParams = new HashMap<String, Object>();
        loadFilterParams(jrParams, report, params);
        loadReportParams(jrParams, report, jrTemplatePath, jsonContent);
        log.debug("Report: {}. Start export JR process.", report.getId());
        long t1 = System.currentTimeMillis();
        doJRExport(jrTemplatePath, expType, jrParams, strTmpFileName);
        log.debug("Report: {}. Finish export JR process. Elapsed time: {}", report.getId(),
                System.currentTimeMillis() - t1);

        final JSONObject recordInfo = new JSONObject();
        params.put("processId", parameters.get("processId"));
        params.put("reportId", parameters.get("reportId"));
        params.put("actionHandler", this.getClass().getName());
        recordInfo.put("processParameters", params);
        recordInfo.put("tmpfileName", strTmpFileName);
        recordInfo.put("fileName", strFileName);

        final JSONObject reportAction = new JSONObject();
        reportAction.put("OBUIAPP_downloadReport", recordInfo);

        final JSONArray actions = new JSONArray();
        actions.put(0, reportAction);
        result.put("responseActions", actions);
    }

    /**
     * Override this method to add validations to the report before it is generated.
     * 
     * @param report
     *          the Report Definition
     * @param parameters
     *          Map including the parameters of the call.
     * @param jsonContent
     *          JSONObject with the values set in the filter parameters.
     */
    protected void doValidations(ReportDefinition report, Map<String, Object> parameters, JSONObject jsonContent)
            throws OBException {
    }

    /**
     * Method that loads the values used in the filter to include them in the parameters that are sent
     * to JasperReports
     * 
     * @param jrParams
     *          the Map instance with all the parameters to be sent to Jasper Reports.
     * @param report
     *          the Report Definition.
     * @param params
     *          JSONObject with the values set in the filter parameters.
     */
    private void loadFilterParams(HashMap<String, Object> jrParams, ReportDefinition report, JSONObject params)
            throws JSONException {
        for (Parameter param : report.getProcessDefintion().getOBUIAPPParameterList()) {
            String paramName = param.getDBColumnName();

            if (params.isNull(paramName)) {
                jrParams.put(paramName, null);
                continue;
            }

            DomainType baseDomainType = ModelProvider.getInstance().getReference(param.getReference().getId())
                    .getDomainType();
            DomainType domainType = null;
            if (param.getReferenceSearchKey() != null) {
                domainType = ModelProvider.getInstance().getReference(param.getReferenceSearchKey().getId())
                        .getDomainType();
            }

            if (baseDomainType instanceof ForeignKeyDomainType) {
                Entity referencedEntity = ((ForeignKeyDomainType) domainType)
                        .getForeignKeyColumn(param.getDBColumnName()).getProperty().getEntity();
                UIDefinition uiDefinition = UIDefinitionController.getInstance()
                        .getUIDefinition(param.getReference());
                JSONObject def = new JSONObject();

                if (uiDefinition instanceof FKMultiSelectorUIDefinition) {
                    JSONArray selectedValues = params.getJSONArray(paramName);
                    JSONArray selectedIdentifiers = new JSONArray();
                    String strValues = "";
                    String strIdentifiers = "";

                    for (int i = 0; i < selectedValues.length(); i++) {
                        if (i > 0) {
                            strValues += ", ";
                            strIdentifiers += ", ";
                        }
                        String value = selectedValues.getString(i);
                        strValues += "'" + value + "'";
                        BaseOBObject record = OBDal.getInstance().get(referencedEntity.getName(), value);
                        if (record != null) {
                            String identifier = record.getIdentifier();
                            strIdentifiers += identifier;
                            selectedIdentifiers.put(identifier);
                        }
                    }
                    def.put("values", selectedValues);
                    def.put("identifiers", selectedIdentifiers);
                    def.put("strValues", strValues);
                    def.put("strIdentifiers", strIdentifiers);
                    jrParams.put(paramName, def);
                } else {
                    String value = params.getString(paramName);
                    BaseOBObject record = OBDal.getInstance().get(referencedEntity.getName(), value);
                    if (record != null) {
                        String identifier = record.getIdentifier();
                        def.put("value", value);
                        def.put("identifier", identifier);
                    }
                }
                jrParams.put(paramName, def);
            } else if (baseDomainType.getClass().equals(StringEnumerateDomainType.class)) {
                // List reference
                String value = params.getString(paramName);
                String identifier = "";
                for (org.openbravo.model.ad.domain.List list : param.getReferenceSearchKey().getADListList()) {
                    if (list.getSearchKey().equals(value)) {
                        identifier = list.getName();
                        break;
                    }
                }
                JSONObject def = new JSONObject();
                def.put("value", value);
                def.put("identifier", identifier);
                jrParams.put(paramName, def);
            } else if (baseDomainType.getClass().equals(StringDomainType.class)) {
                jrParams.put(paramName, params.getString(paramName));
            } else if (baseDomainType.getClass().equals(DateDomainType.class)) {
                DateDomainType dateDomainType = (DateDomainType) baseDomainType;
                Date date = (Date) dateDomainType.createFromString(params.getString(paramName));
                jrParams.put(paramName, date);
            } else if (baseDomainType.getClass().getSuperclass().equals(BigDecimalDomainType.class)
                    || baseDomainType.getClass().equals(LongDomainType.class)) {
                jrParams.put(paramName, new BigDecimal(params.getString(paramName)));
            } else if (baseDomainType.getClass().equals(BooleanDomainType.class)) {
                jrParams.put(paramName, params.getBoolean(paramName));
            } else { // default
                jrParams.put(paramName, params.getString(paramName));
            }

        }
    }

    /**
     * Method to load the generic parameters that are sent to Jasper Reports.
     * 
     * @param jrParams
     *          the Map instance with all the parameters to be sent to Jasper Reports.
     * @param report
     *          the Report Definition.
     * @param jrTemplatePath
     *          String with the path where the jr template is stored in the server.
     * @param jsonContent
     *          JSONObject with the values set in the filter parameters.
     */
    private void loadReportParams(HashMap<String, Object> jrParams, ReportDefinition report, String jrTemplatePath,
            JSONObject jsonContent) {

        final int lastSegmentIndex = jrTemplatePath.lastIndexOf("/");
        final String fileDir;
        if (lastSegmentIndex != -1) {
            fileDir = jrTemplatePath.substring(0, lastSegmentIndex + 1);
        } else {
            fileDir = "";
        }
        jrParams.put("SUBREPORT_DIR", fileDir);
        jrParams.put(JASPER_PARAM_PROCESS, report.getProcessDefintion());

        addAdditionalParameters(report, jsonContent, jrParams);
    }

    /**
     * @return the filename of the generated file, it is okay to return null, then a default filename
     *         is generated. NOTE, if you return a value it should include the extension. Also, it can
     *         make sense to use the {@link #getSafeFilename(String)} method to ensure the file name
     *         is valid
     */
    private String getPDFFileName(ReportDefinition report, Map<String, Object> parameters, ExportType expType) {
        final SimpleDateFormat dateFormat = new SimpleDateFormat(
                OBPropertiesProvider.getInstance().getOpenbravoProperties().getProperty("dateTimeFormat.java"));
        return getSafeFilename(report.getProcessDefintion().getName() + "-" + dateFormat.format(new Date())) + "."
                + expType.getExtension();
    }

    private static String getSafeFilename(String name) {
        return name.replaceAll("[:\\\\/*?|<>]", "_");
    }

    /**
     * Override this method to put additional parameters to send to the Jasper Report template.
     * Process Definition filter parameters are automatically added.
     * 
     * @param process
     *          the Process Definition of the Report
     * @param jsonContent
     *          values set in the filter parameters
     * @param parameters
     *          the current Parameter Map that it is send to the Jasper Report.
     */
    protected void addAdditionalParameters(ReportDefinition process, JSONObject jsonContent,
            Map<String, Object> parameters) {
    }

    private static void doJRExport(String jrTemplatePath, ExportType expType, Map<String, Object> parameters,
            String strFileName) {
        ReportSemaphoreHandling.getInstance().acquire();
        try {
            ReportingUtils.exportJR(jrTemplatePath, expType, parameters, strFileName);
        } finally {
            ReportSemaphoreHandling.getInstance().release();
        }
    }
}