From 162c48ec4cdda9fc3fd15b065aadaaa50cb1c4e6 Mon Sep 17 00:00:00 2001 From: hengsin Date: Thu, 22 Jul 2021 17:42:07 +0800 Subject: [PATCH] IDEMPIERE-4873 Jasper Report Starter Refactoring (#779) --- .../src/org/compiere/util/Util.java | 85 +- .../META-INF/MANIFEST.MF | 5 +- .../adempiere/report/jasper/Activator.java | 54 + .../jasper/AttachmentResourceLoader.java | 191 +++ .../report/jasper/BundleResourceLoader.java | 229 +++ .../report/jasper/ClassResourceLoader.java | 222 +++ .../report/jasper/FileResourceLoader.java | 136 ++ .../report/jasper/ReportStarter.java | 1516 ++++++----------- .../report/jasper/WebResourceLoader.java | 241 +++ .../src/org/adempiere/webui/apps/AEnv.java | 46 +- .../adempiere/webui/window/ZkJRViewer.java | 3 +- 11 files changed, 1709 insertions(+), 1019 deletions(-) create mode 100644 org.adempiere.report.jasper/src/org/adempiere/report/jasper/Activator.java create mode 100644 org.adempiere.report.jasper/src/org/adempiere/report/jasper/AttachmentResourceLoader.java create mode 100644 org.adempiere.report.jasper/src/org/adempiere/report/jasper/BundleResourceLoader.java create mode 100644 org.adempiere.report.jasper/src/org/adempiere/report/jasper/ClassResourceLoader.java create mode 100644 org.adempiere.report.jasper/src/org/adempiere/report/jasper/FileResourceLoader.java create mode 100644 org.adempiere.report.jasper/src/org/adempiere/report/jasper/WebResourceLoader.java diff --git a/org.adempiere.base/src/org/compiere/util/Util.java b/org.adempiere.base/src/org/compiere/util/Util.java index cd1fe41deb..722ec24306 100644 --- a/org.adempiere.base/src/org/compiere/util/Util.java +++ b/org.adempiere.base/src/org/compiere/util/Util.java @@ -16,16 +16,20 @@ *****************************************************************************/ package org.compiere.util; -import java.awt.Color; -import java.awt.font.TextAttribute; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; import java.io.UnsupportedEncodingException; import java.sql.Timestamp; import java.text.AttributedCharacterIterator; import java.text.AttributedString; import java.text.Normalizer; +import java.util.ArrayList; import java.util.Calendar; import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; @@ -37,6 +41,13 @@ import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.KeyStroke; +import com.itextpdf.text.Document; +import com.itextpdf.text.DocumentException; +import com.itextpdf.text.pdf.PdfContentByte; +import com.itextpdf.text.pdf.PdfImportedPage; +import com.itextpdf.text.pdf.PdfReader; +import com.itextpdf.text.pdf.PdfWriter; + /** * General Utilities * @@ -654,26 +665,6 @@ public class Util return str; } // trimSize - - /************************************************************************** - * Test - * @param args args - */ - public static void main (String[] args) - { - String str = "a�b�c?d?e?f?g?"; - System.out.println(str + " = " + str.length() + " - " + size(str)); - String str1 = trimLength(str, 10); - System.out.println(str1 + " = " + str1.length() + " - " + size(str1)); - String str2 = trimSize(str, 10); - System.out.println(str2 + " = " + str2.length() + " - " + size(str2)); - // - AttributedString aString = new AttributedString ("test test"); - aString.addAttribute(TextAttribute.FOREGROUND, Color.blue); - aString.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON, 2, 4); - getIterator (aString, new AttributedCharacterIterator.Attribute[] {TextAttribute.UNDERLINE}); - } // main - /** * String diacritics from given string * @param s original string @@ -707,4 +698,54 @@ public class Util cal.set(Calendar.MILLISECOND, 0); return new Timestamp(cal.getTimeInMillis()); } + + /** + * + * @param pdfList + * @param outFile + * @throws IOException + * @throws DocumentException + * @throws FileNotFoundException + */ + public static void mergePdf(List pdfList, File outFile) throws IOException, + DocumentException, FileNotFoundException { + Document document = null; + PdfWriter copy = null; + + List pdfReaders = new ArrayList(); + + try + { + for (File f : pdfList) + { + PdfReader reader = new PdfReader(f.getAbsolutePath()); + + pdfReaders.add(reader); + + if (document == null) + { + document = new Document(reader.getPageSizeWithRotation(1)); + copy = PdfWriter.getInstance(document, new FileOutputStream(outFile)); + document.open(); + } + int pages = reader.getNumberOfPages(); + PdfContentByte cb = copy.getDirectContent(); + for (int i = 1; i <= pages; i++) { + document.newPage(); + copy.newPage(); + PdfImportedPage page = copy.getImportedPage(reader, i); + cb.addTemplate(page, 0, 0); + copy.releaseTemplate(page); + } + } + document.close(); + } + finally + { + for(PdfReader reader:pdfReaders) + { + reader.close(); + } + } + } } // Util diff --git a/org.adempiere.report.jasper/META-INF/MANIFEST.MF b/org.adempiere.report.jasper/META-INF/MANIFEST.MF index d23ab11707..d3312c4a34 100644 --- a/org.adempiere.report.jasper/META-INF/MANIFEST.MF +++ b/org.adempiere.report.jasper/META-INF/MANIFEST.MF @@ -12,12 +12,15 @@ Import-Package: net.sourceforge.barbecue, org.apache.commons.logging.impl;version="1.1.1", org.jfree.chart, org.jfree.chart.plot, + org.osgi.framework;version="1.10.0", org.osgi.service.event Require-Bundle: org.adempiere.base;bundle-version="0.0.0", - net.sf.jasperreports.engine;bundle-version="6.3.1" + net.sf.jasperreports.engine;bundle-version="6.3.1", + com.itextpdf;bundle-version="5.5.13" Service-Component: OSGI-INF/*.xml Export-Package: org.adempiere.report.jasper Bundle-ActivationPolicy: lazy Bundle-ClassPath: . Automatic-Module-Name: org.adempiere.report.jasper Bundle-Vendor: iDempiere Community +Bundle-Activator: org.adempiere.report.jasper.Activator diff --git a/org.adempiere.report.jasper/src/org/adempiere/report/jasper/Activator.java b/org.adempiere.report.jasper/src/org/adempiere/report/jasper/Activator.java new file mode 100644 index 0000000000..7e517744b8 --- /dev/null +++ b/org.adempiere.report.jasper/src/org/adempiere/report/jasper/Activator.java @@ -0,0 +1,54 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * 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., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + * Contributors: * + * - hengsin * + **********************************************************************/ +package org.adempiere.report.jasper; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; + +/** + * @author hengsin + * + */ +public class Activator implements BundleActivator { + + private static BundleContext bundleContext = null; + + @Override + public void start(BundleContext context) throws Exception { + bundleContext = context; + } + + @Override + public void stop(BundleContext context) throws Exception { + bundleContext = null; + } + + /** + * @return bundle context + */ + public static BundleContext getBundleContext() { + return bundleContext; + } +} diff --git a/org.adempiere.report.jasper/src/org/adempiere/report/jasper/AttachmentResourceLoader.java b/org.adempiere.report.jasper/src/org/adempiere/report/jasper/AttachmentResourceLoader.java new file mode 100644 index 0000000000..e035f41cc7 --- /dev/null +++ b/org.adempiere.report.jasper/src/org/adempiere/report/jasper/AttachmentResourceLoader.java @@ -0,0 +1,191 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * 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., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + * Contributors: * + * - hengsin * + **********************************************************************/ +package org.adempiere.report.jasper; + +import java.io.File; +import java.util.ArrayList; + +import org.adempiere.exceptions.AdempiereException; +import org.compiere.model.MAttachment; +import org.compiere.model.MAttachmentEntry; +import org.compiere.model.MProcess; +import org.compiere.process.ProcessInfo; +import org.compiere.util.CLogger; +import org.compiere.util.Env; +import org.compiere.util.Language; +import org.compiere.utils.DigestOfFile; + +/** + * Load report resources from attachment to process + * @author hengsin + * + */ +public class AttachmentResourceLoader { + + private final static CLogger log = CLogger.getCLogger(AttachmentResourceLoader.class); + + private String destinationFolder; + + private MAttachment attachment; + + private static final String ATTACHMENT_PATH_PREFIX = "attachment:"; + + /** + * + * @param destinationFolder + */ + public AttachmentResourceLoader(String destinationFolder) { + this.destinationFolder = destinationFolder; + } + + /** + * Get report file from process attachment + * + * @param reportPath must of syntax attachment:filename + * @return File + */ + public File getReportFile(ProcessInfo processInfo, String reportPath) { + File reportFile = null; + String name = reportPath.substring(ATTACHMENT_PATH_PREFIX.length()).trim(); + MProcess process = new MProcess(Env.getCtx(), processInfo.getAD_Process_ID(), processInfo.getTransactionName()); + attachment = process.getAttachment(); + if (attachment != null) { + MAttachmentEntry[] entries = attachment.getEntries(); + MAttachmentEntry entry = null; + for (int i = 0; i < entries.length; i++) { + if (entries[i].getName().equals(name)) { + entry = entries[i]; + break; + } + } + if (entry != null) { + reportFile = getAttachmentEntryFile(entry); + } + } + return reportFile; + } + + /** + * Download db attachment to local file + * + * @param entry + * @return File + */ + private File getAttachmentEntryFile(MAttachmentEntry entry) { + String localFile = destinationFolder + entry.getName(); + String downloadedLocalFile = destinationFolder + "TMP_" + entry.getName(); + File reportFile = new File(localFile); + if (reportFile.exists()) { + String localMD5hash = DigestOfFile.getMD5Hash(reportFile); + String entryMD5hash = DigestOfFile.getMD5Hash(entry.getData()); + if (localMD5hash.equals(entryMD5hash)) { + log.info(" no need to download: local report is up-to-date"); + } else { + log.info(" report on server is different from local copy, download and replace"); + File downloadedFile = new File(downloadedLocalFile); + entry.getFile(downloadedFile); + if (!reportFile.delete()) { + throw new AdempiereException("Cannot delete temporary file " + reportFile.toString()); + } + if (!downloadedFile.renameTo(reportFile)) { + throw new AdempiereException("Cannot rename temporary file " + downloadedFile.toString() + " to " + + reportFile.toString()); + } + } + } else { + entry.getFile(reportFile); + } + return reportFile; + } + + private File getAttachmentEntryFile(String resname) { + File fileattach = null; + MAttachmentEntry[] entries = attachment.getEntries(); + for (int i = 0; i < entries.length; i++) { + if (entries[i].getName().equals(resname)) { + fileattach = getAttachmentEntryFile(entries[i]); + break; + } + } + return fileattach; + } + + /** + * Get property resource file from process attachment + * + * @param bundleName + * @param currLang + * @return File + */ + public File getResourceBundle(String bundleName, Language currLang) { + String resname = bundleName + "_" + currLang.getLocale().getLanguage() + "_" + currLang.getLocale().getCountry() + + ".properties"; + File resFile = getAttachmentEntryFile(resname); + if (resFile == null) { + resname = bundleName + "_" + currLang.getLocale().getLanguage() + ".properties"; + resFile = getAttachmentEntryFile(resname); + if (resFile == null) { + resname = bundleName + ".properties"; + resFile = getAttachmentEntryFile(resname); + } + } + return resFile; + } + + /** + * Get subreports and other resources (for e.g images) from attachment. Assume all other jasper attachment is + * subreport. + * + * @param reportPath + * @return File[] of subreports + */ + public File[] getSubreports(String reportPath) { + String name = reportPath.substring(ATTACHMENT_PATH_PREFIX.length()).trim(); + ArrayList subreports = new ArrayList(); + MAttachmentEntry[] entries = attachment.getEntries(); + for (int i = 0; i < entries.length; i++) { + // @Trifon + if (!entries[i].getName().equals(name)) { + File reportFile = getAttachmentEntryFile(entries[i]); + if (reportFile != null) { + if (entries[i].getName().toLowerCase().endsWith(".jrxml") + || entries[i].getName().toLowerCase().endsWith(".jasper")) { + subreports.add(reportFile); + } + } + } + } + return subreports.toArray(new File[0]); + } + + /** + * + * @param path + * @return true if path starts with "attachment:" + */ + public static boolean isAttachmentResourcePath(String path) { + return path != null && path.startsWith(AttachmentResourceLoader.ATTACHMENT_PATH_PREFIX); + } +} diff --git a/org.adempiere.report.jasper/src/org/adempiere/report/jasper/BundleResourceLoader.java b/org.adempiere.report.jasper/src/org/adempiere/report/jasper/BundleResourceLoader.java new file mode 100644 index 0000000000..495d25d018 --- /dev/null +++ b/org.adempiere.report.jasper/src/org/adempiere/report/jasper/BundleResourceLoader.java @@ -0,0 +1,229 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * 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., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + * Contributors: * + * - hengsin * + **********************************************************************/ +package org.adempiere.report.jasper; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.Enumeration; +import java.util.logging.Level; + +import org.compiere.util.CLogger; +import org.compiere.util.Language; +import org.compiere.utils.DigestOfFile; + +/** + * + * Get report resources from fragment bundle + * @author hengsin + * + */ +public class BundleResourceLoader { + + private final static CLogger log = CLogger.getCLogger(BundleResourceLoader.class); + + private String destinationFolder; + + private String resourcePath; + + private static final String BUNDLE_PATH_PREFIX = "bundle:"; + + /** + * + * @param destinationFolder + */ + public BundleResourceLoader(String destinationFolder) { + this.destinationFolder = destinationFolder; + } + + /** + * @param path + * @return File or URL + */ + public Object getResource(String path) { + String resourceName = path.startsWith(BUNDLE_PATH_PREFIX) ? path.substring(BUNDLE_PATH_PREFIX.length()).trim() : path.trim(); + + File reportFile = null; + boolean empty = true; + String parentPath = "/"; + String bundleFileName = resourceName; + if (resourceName.lastIndexOf("/") > 0) { + parentPath = "/"+resourceName.substring(0, resourceName.lastIndexOf("/")); + bundleFileName = resourceName.substring(resourceName.lastIndexOf("/")+1); + } + URL url = null; + Enumeration entries = Activator.getBundleContext().getBundle().findEntries(parentPath, bundleFileName, false); + if (entries != null && entries.hasMoreElements()) + url = entries.nextElement(); + if (url != null) { + if (resourceName.endsWith(".jrxml")) { + File tmpOutputFile = null; + try (InputStream inputStream = url.openStream()) { + if (inputStream != null) { + String localResourceName = toLocalName(resourceName); + String localAbsoluteFileName = destinationFolder + localResourceName; + String localAbsolutePathName = localAbsoluteFileName.substring(0, localAbsoluteFileName.lastIndexOf(System.getProperty("file.separator"))+1); + Path localPath = Path.of(localAbsolutePathName); + try { + if (!Files.exists(localPath)) + Files.createDirectory(localPath); + } catch (IOException e) { + throw new RuntimeException(e); + } + String localFileName = localAbsoluteFileName.substring(localAbsoluteFileName.lastIndexOf(System.getProperty("file.separator"))+1); + String extension = localFileName.substring(localFileName.lastIndexOf(".")); + reportFile = new File(localAbsoluteFileName); + if (!reportFile.exists()) { + reportFile.createNewFile(); + tmpOutputFile = reportFile; + } else { + tmpOutputFile = File.createTempFile(localFileName.substring(0, localFileName.lastIndexOf(".")), extension, localPath.toFile()); + } + try (OutputStream out = new FileOutputStream(tmpOutputFile);) { + if (out != null) { + byte buf[] = new byte[1024]; + int len; + while ((len = inputStream.read(buf)) > 0) { + empty = false; + out.write(buf, 0, len); + } + } + } + } + } catch (Exception e) { + log.log(Level.SEVERE, e.getMessage(), e); + empty = true; + } + if (!empty && tmpOutputFile != reportFile) { + if (!DigestOfFile.md5HashCompare(reportFile, tmpOutputFile)) { + Path to = reportFile.toPath(); + Path from = tmpOutputFile.toPath(); + try { + Files.copy(from, to, StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + log.log(Level.SEVERE, e.getMessage(), e); + return null; + } + } + try { + Files.delete(tmpOutputFile.toPath()); + } catch (IOException e) {} + } + } else { + empty = false; + } + } + + if (empty) + return null; + + if (url != null) { + resourcePath = url.toString(); + if (resourcePath != null && resourcePath.lastIndexOf("/") > 0) + resourcePath = resourcePath.substring(0, resourcePath.lastIndexOf("/")+1); + } + + return reportFile != null ? reportFile : url; + } + + private String toLocalName(String name) { + String localName = name; + if (localName.startsWith("/")) + localName = localName.substring(1); + if (localName.lastIndexOf("/") > 0) { + String path = localName.substring(0, localName.lastIndexOf("/")); + localName = localName.substring(localName.lastIndexOf("/")+1); + path = path.replace('/', '_'); + localName = path + System.getProperty("file.separator") + localName; + } + return localName; + } + + /** + * Get property resource file from resources + * + * @param bundleName + * @param currLang + * @return URL + */ + public URL getResourceBundle(String bundleName, Language currLang) { + String resname = bundleName + "_" + currLang.getLocale().getLanguage() + "_" + currLang.getLocale().getCountry() + + ".properties"; + URL resourceURL = null; + try { + resourceURL = (URL) getResource(resname); + } catch (Exception e) { + // ignore exception - file couldn't exist + } + if (resourceURL == null) { + resname = bundleName + "_" + currLang.getLocale().getLanguage() + ".properties"; + try { + resourceURL = (URL) getResource(resname); + } catch (Exception e) { + // ignore exception - file couldn't exist + } + if (resourceURL == null) { + resname = bundleName + ".properties"; + try { + resourceURL = (URL) getResource(resname); + } catch (Exception e) { + // ignore exception - file couldn't exist + } + } + } + return resourceURL; + } + + /** + * @param path The full path to the parent report + * @return File[0]; + */ + public File[] getSubreports(String path) { + return new File[0]; + } + + /** + * + * @return url path to load subreports, images and resource bundle + */ + public String getRelatedResourcesPath() { + return resourcePath; + } + + /** + * + * @param path + * @return true if path is "bundle:*" + */ + public static boolean isBundleResourcePath(String path) { + return path != null && path.startsWith(BUNDLE_PATH_PREFIX); + } +} diff --git a/org.adempiere.report.jasper/src/org/adempiere/report/jasper/ClassResourceLoader.java b/org.adempiere.report.jasper/src/org/adempiere/report/jasper/ClassResourceLoader.java new file mode 100644 index 0000000000..eb38b09dcb --- /dev/null +++ b/org.adempiere.report.jasper/src/org/adempiere/report/jasper/ClassResourceLoader.java @@ -0,0 +1,222 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * 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., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + * Contributors: * + * - hengsin * + **********************************************************************/ +package org.adempiere.report.jasper; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.logging.Level; + +import org.compiere.util.CLogger; +import org.compiere.util.Language; +import org.compiere.utils.DigestOfFile; + +/** + * + * Get report resources from class path + * @author hengsin + * + */ +public class ClassResourceLoader { + + private final static CLogger log = CLogger.getCLogger(ClassResourceLoader.class); + + private String destinationFolder; + + private String resourcePath; + + private static final String RESOURCE_PATH_PREFIX = "resource:"; + + /** + * + * @param destinationFolder + */ + public ClassResourceLoader(String destinationFolder) { + this.destinationFolder = destinationFolder; + } + + /** + * @param path + * @return File or URL + */ + public Object getResource(String path) { + boolean empty = true; + URL url = null; + File reportFile = null; + String resourceName = path.startsWith(RESOURCE_PATH_PREFIX) ? path.substring(RESOURCE_PATH_PREFIX.length()).trim() : path.trim(); + //only copy to tmp if reportPath is jrxml file (for compilation) + if (resourceName.endsWith(".jrxml")) { + String localResourceName = toLocalName(resourceName); + String localAbsolutePathName = destinationFolder + localResourceName; + reportFile = new File(localAbsolutePathName); + String localPathName = localAbsolutePathName.substring(0, localAbsolutePathName.lastIndexOf(System.getProperty("file.separator"))+1); + Path localAbsolutePath = Path.of(localPathName); + try { + if (!Files.exists(localAbsolutePath)) + Files.createDirectory(localAbsolutePath); + } catch (IOException e) { + throw new RuntimeException(e); + } + String localFileName = localAbsolutePathName.substring(localAbsolutePathName.lastIndexOf(System.getProperty("file.separator"))+1); + String extension = localFileName.substring(localFileName.lastIndexOf(".")); + File tmpOutputFile = null; + + url = getClass().getClassLoader().getResource(resourceName); + if (url != null) { + try (InputStream inputStream = url.openStream()) { + if (inputStream != null) { + if (!reportFile.exists()) { + reportFile.createNewFile(); + tmpOutputFile = reportFile; + } else { + tmpOutputFile = File.createTempFile(localFileName.substring(0, localFileName.lastIndexOf(".")), extension, localAbsolutePath.toFile()); + } + try (OutputStream out = new FileOutputStream(tmpOutputFile);) { + if (out != null) { + byte buf[] = new byte[1024]; + int len; + while ((len = inputStream.read(buf)) > 0) { + empty = false; + out.write(buf, 0, len); + } + } + } + } + } catch (Exception e) { + log.log(Level.SEVERE, e.getMessage(), e); + empty = true; + } + if (!empty && tmpOutputFile != reportFile) { + if (!DigestOfFile.md5HashCompare(reportFile, tmpOutputFile)) { + Path to = reportFile.toPath(); + Path from = tmpOutputFile.toPath(); + try { + Files.copy(from, to, StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + log.log(Level.SEVERE, e.getMessage(), e); + return null; + } + } + try { + Files.delete(tmpOutputFile.toPath()); + } catch (IOException e) {} + } + } + } else { + url = getClass().getClassLoader().getResource(resourceName); + empty = (url == null); + } + + if (empty) + return null; + + if (url != null) { + resourcePath = url.toString(); + if (resourcePath != null && resourcePath.lastIndexOf("/") > 0) + resourcePath = resourcePath.substring(0, resourcePath.lastIndexOf("/")+1); + } + + return reportFile != null ? reportFile : url; + } + + private String toLocalName(String name) { + String localName = name; + if (localName.startsWith("/")) + localName = localName.substring(1); + if (localName.lastIndexOf("/") > 0) { + String path = localName.substring(0, localName.lastIndexOf("/")); + localName = localName.substring(localName.lastIndexOf("/")+1); + path = path.replace('/', '_'); + localName = path + System.getProperty("file.separator") + localName; + } + return localName; + } + + /** + * Get property resource file from resources + * + * @param bundleName + * @param currLang + * @return URL + */ + public URL getResourceBundle(String bundleName, Language currLang) { + String resourceName = bundleName + "_" + currLang.getLocale().getLanguage() + "_" + currLang.getLocale().getCountry() + + ".properties"; + URL resourceURL = null; + try { + resourceURL = (URL) getResource(resourceName); + } catch (Exception e) { + // ignore exception - file couldn't exist + } + if (resourceURL == null) { + resourceName = bundleName + "_" + currLang.getLocale().getLanguage() + ".properties"; + try { + resourceURL = (URL) getResource(resourceName); + } catch (Exception e) { + // ignore exception - file couldn't exist + } + if (resourceURL == null) { + resourceName = bundleName + ".properties"; + try { + resourceURL = (URL) getResource(resourceName); + } catch (Exception e) { + // ignore exception - file couldn't exist + } + } + } + return resourceURL; + } + + /** + * @param path The full path to the parent report + * @return File[0] + */ + public File[] getSubreports(String path) { + return new File[0]; + } + + /** + * + * @return url path to load subreports, images and resource bundle + */ + public String getRelatedResourcesPath() { + return resourcePath; + } + + /** + * + * @param path + * @return true if path is "resource:*" + */ + public static boolean isClassResourcePath(String path) { + return path != null && path.startsWith(RESOURCE_PATH_PREFIX); + } +} diff --git a/org.adempiere.report.jasper/src/org/adempiere/report/jasper/FileResourceLoader.java b/org.adempiere.report.jasper/src/org/adempiere/report/jasper/FileResourceLoader.java new file mode 100644 index 0000000000..04228b21a5 --- /dev/null +++ b/org.adempiere.report.jasper/src/org/adempiere/report/jasper/FileResourceLoader.java @@ -0,0 +1,136 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * 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., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + * Contributors: * + * - hengsin * + **********************************************************************/ +package org.adempiere.report.jasper; + +import java.io.File; +import java.io.FilenameFilter; +import java.net.URI; +import java.net.URISyntaxException; + +import org.compiere.util.CLogger; +import org.compiere.util.Language; + +/** + * + * @author hengsin + * + */ +public class FileResourceLoader { + + private final static CLogger log = CLogger.getCLogger(FileResourceLoader.class); + + private static final String FILE_PATH_PREFIX = "file:/"; + + public FileResourceLoader() { + } + + /** + * + * @param reportPath + * @return File + */ + public File getReportFile(String reportPath) { + File reportFile = null; + try { + reportFile = new File(new URI(reportPath)); + } catch (URISyntaxException e) { + log.warning(e.getLocalizedMessage()); + reportFile = null; + } + return reportFile; + } + + /** + * Get property resource file from file:// + * + * @param resourcePath + * @param bundleName + * @param currLang + * @return File + */ + public File getResourceBundle(String resourcePath, String bundleName, Language currLang) { + String resname = bundleName + "_" + currLang.getLocale().getLanguage() + "_" + currLang.getLocale().getCountry() + + ".properties"; + File resFile = new File(resourcePath, resname); + if (!resFile.exists()) { + resname = bundleName + "_" + currLang.getLocale().getLanguage() + ".properties"; + resFile = new File(resourcePath, resname); + if (!resFile.exists()) { + resname = bundleName + ".properties"; + resFile = new File(resourcePath, resname); + if (!resFile.exists()) { + resFile = null; + } + } + } + return resFile; + } + + /** + * + * @param reportFile + * @param reportDir + * @return File[] + */ + public File[] getSubreports(File reportFile, File reportDir) { + return reportDir.listFiles(new FileFilter(reportFile, reportDir)); + } + + /** + * + * @param path + * @return true if path is "file:*" + */ + public static boolean isFileResourcePath(String path) { + return path != null && path.startsWith(FILE_PATH_PREFIX); + } + + private static class FileFilter implements FilenameFilter { + private File directory; + private File reportFile; + + /** + * + * @param reportFile + * @param directory + */ + public FileFilter(File reportFile, File directory) { + this.reportFile = reportFile; + this.directory = directory; + } + + @Override + public boolean accept(File file, String name) { + if (file.equals(directory)) { + if (!name.equals(reportFile.getName())) { + String lower = name.toLowerCase(); + if (lower.endsWith(".jasper") || lower.endsWith(".jrxml")) + return true; + } + } + return false; + } + } +} diff --git a/org.adempiere.report.jasper/src/org/adempiere/report/jasper/ReportStarter.java b/org.adempiere.report.jasper/src/org/adempiere/report/jasper/ReportStarter.java index 9d622fa7e7..a858718cef 100644 --- a/org.adempiere.report.jasper/src/org/adempiere/report/jasper/ReportStarter.java +++ b/org.adempiere.report.jasper/src/org/adempiere/report/jasper/ReportStarter.java @@ -14,23 +14,16 @@ package org.adempiere.report.jasper; import java.awt.print.PrinterJob; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.FileOutputStream; -import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.io.Serializable; import java.math.BigDecimal; -import java.net.URI; -import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.StandardCopyOption; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -44,6 +37,9 @@ import java.util.Map; import java.util.Properties; import java.util.PropertyResourceBundle; import java.util.logging.Level; +import java.util.zip.Deflater; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; import javax.print.attribute.HashPrintRequestAttributeSet; import javax.print.attribute.PrintRequestAttributeSet; @@ -55,8 +51,6 @@ import org.adempiere.base.Service; import org.adempiere.exceptions.AdempiereException; import org.adempiere.exceptions.DBException; import org.adempiere.util.IProcessUI; -import org.compiere.model.MAttachment; -import org.compiere.model.MAttachmentEntry; import org.compiere.model.MProcess; import org.compiere.model.MQuery; import org.compiere.model.MSysConfig; @@ -78,9 +72,7 @@ import org.compiere.util.Language; import org.compiere.util.Msg; import org.compiere.util.Trx; import org.compiere.util.Util; -import org.compiere.utils.DigestOfFile; -import net.sf.jasperreports.engine.DefaultJasperReportsContext; import net.sf.jasperreports.engine.JRException; import net.sf.jasperreports.engine.JRParameter; import net.sf.jasperreports.engine.JRPropertiesUtil; @@ -127,10 +119,6 @@ import net.sf.jasperreports.export.SimpleXmlExporterOutput; /** * @author rlemeill - * Originally coming from an application note from compiere.co.uk - * --- - * Modifications: Allow Jasper Reports to be able to be run on VPN profile (i.e: no direct connection to DB). - * Implemented ClientProcess for it to run on client. * @author Ashley Ramdass * @author victor.perez@e-evolution.com * @see FR 1906632 http://sourceforge.net/tracker/?func=detail&atid=879335&aid=1906632&group_id=176962 @@ -141,11 +129,17 @@ import net.sf.jasperreports.export.SimpleXmlExporterOutput; */ public class ReportStarter implements ProcessCall, ClientProcess { + public static final String IDEMPIERE_REPORT_TYPE = "IDEMPIERE_REPORT_TYPE"; + + private static final String SUBREPORT_DIR = "SUBREPORT_DIR"; + private static final String COLUMN_LOOKUP = "COLUMN_LOOKUP"; + private static final String CURRENT_LANG = "CURRENT_LANG"; + private static final String RESOURCE_DIR = "RESOURCE_DIR"; private static final int DEFAULT_SWAP_MAX_PAGES = 100; /** Logger */ private static final CLogger log = CLogger.getCLogger(ReportStarter.class); private static File REPORT_HOME = null; - public static final JasperReportsContext jasperReportStartContext; + private static final JasperReportsContext jasperReportContext; static { String reportPath = System.getProperty("org.compiere.report.path"); @@ -157,201 +151,29 @@ public class ReportStarter implements ProcessCall, ClientProcess // SimpleJasperReportsContext just same like DefaultJasperReportsContext, but DefaultJasperReportsContext is singleton, // every thing setting for ReportStarter will effect to other "customize" jasper engine - jasperReportStartContext = new SimpleJasperReportsContext(); + jasperReportContext = new SimpleJasperReportsContext(); // http://jasperreports.sourceforge.net/sample.reference/groovy/index.html#javaCompilers // http://jasperreports.sourceforge.net/api/net/sf/jasperreports/engine/JasperCompileManager.html // default is 1.8 but jasper will don't understand and break autobox feature // other value (org.eclipse.jdt.core.compiler.compliance, org.eclipse.jdt.core.compiler.codegen.targetPlatform) still keep 1.8 - jasperReportStartContext.setProperty("org.eclipse.jdt.core.compiler.source", "1.5"); + jasperReportContext.setProperty("org.eclipse.jdt.core.compiler.source", "1.5"); + + JRPropertiesUtil.getInstance(jasperReportContext).setProperty("net.sf.jasperreports.awt.ignore.missing.font", "true"); } private ProcessInfo processInfo; - private MAttachment attachment; @SuppressWarnings("unused") private IProcessUI m_processUI; - /** - * @author rlemeill - * @param reportLocation http://applicationserver/webApp/standalone.jrxml for example - * @param localPath Where to put the http downloaded file - * @return abstract File which represent the downloaded file - */ - private File getRemoteFile(String reportLocation, String localPath) - { - try{ - URL reportURL = new URL(reportLocation); - InputStream in = reportURL.openStream(); - - File downloadedFile = new File(localPath); - - if (downloadedFile.exists()) - { - downloadedFile.delete(); - } - - FileOutputStream fout = new FileOutputStream(downloadedFile); - - byte buf[] = new byte[1024]; - int s = 0; - while((s = in.read(buf, 0, 1024)) > 0) - fout.write(buf, 0, s); - - in.close(); - fout.flush(); - fout.close(); - return downloadedFile; - } catch (FileNotFoundException e) { - return null; - } catch (IOException e) { - throw new AdempiereException("I/O error when trying to download (sub)report from server "+ e.getLocalizedMessage()); - } - } - - /** - * Search for additional subreports deployed to a webcontext if - * the parent report is located there - * @author deathmeat - * @param reportName The original report name - * @param reportPath The full path to the parent report - * @param fileExtension The file extension of the parent report - * @return An Array of File objects referencing to the downloaded subreports - */ - private File[] getHttpSubreports(String reportName, String reportPath, String fileExtension) - { - ArrayList subreports = new ArrayList(); - String remoteDir = reportPath.substring(0, reportPath.lastIndexOf("/")); - - // Currently check hardcoded for max. 10 subreports - for(int i=1; i<10; i++) - { - // Check if subreport number i exists - File subreport = httpDownloadedReport(remoteDir + "/" + reportName + i + fileExtension); - if(subreport == null) // Subreport doesn't exist, abort further approaches - break; - - subreports.add(subreport); - } - - File[] subreportsTemp = new File[0]; - subreportsTemp = subreports.toArray(subreportsTemp); - return subreportsTemp; - } - - /** - * @author rlemeill - * @param reportLocation http string url ex: http://adempiereserver.domain.com/webApp/standalone.jrxml - * @return downloaded File (or already existing one) - */ - private File httpDownloadedReport(String reportLocation) - { - File reportFile = null; - File downloadedFile = null; - if (log.isLoggable(Level.INFO)) log.info(" report deployed to " + reportLocation); - try { - - - String[] tmps = reportLocation.split("/"); - String cleanFile = tmps[tmps.length-1]; - String localFile = getLocalDownloadFolder() + cleanFile; - String downloadedLocalFile = getLocalDownloadFolder() + "TMP_" + cleanFile; - - reportFile = new File(localFile); - if (reportFile.exists()) - { - String remoteMD5Hash = getRemoteMD5(reportLocation); - if (!Util.isEmpty(remoteMD5Hash, true)) - { - String localMD5hash = DigestOfFile.getMD5Hash(reportFile); - if (log.isLoggable(Level.INFO)) log.info("MD5 for local file is "+localMD5hash ); - if (localMD5hash.equals(remoteMD5Hash.trim())) - { - if (log.isLoggable(Level.INFO)) log.info("MD5 match: local report file is up-to-date"); - return reportFile; - } - else - { - if (log.isLoggable(Level.INFO)) log.info("MD5 is different, download and replace"); - downloadedFile = getRemoteFile(reportLocation, downloadedLocalFile); - if (downloadedFile != null) - { - Path to = reportFile.toPath(); - Path from = downloadedFile.toPath(); - Files.copy(from, to, StandardCopyOption.REPLACE_EXISTING); - return to.toFile(); - } - else - { - return null; - } - } - } - else - { - downloadedFile = getRemoteFile(reportLocation, downloadedLocalFile); - if (downloadedFile == null) - return null; - - // compare hash of existing and downloaded - if ( DigestOfFile.md5HashCompare(reportFile,downloadedFile) ) - { - //nothing file are identical - if (log.isLoggable(Level.INFO)) log.info("MD5 match: local report file is up-to-date"); - return reportFile; - } - else - { - if (log.isLoggable(Level.INFO)) log.info("MD5 is different, replace with downloaded file"); - Path to = reportFile.toPath(); - Path from = downloadedFile.toPath(); - Files.copy(from, to, StandardCopyOption.REPLACE_EXISTING); - return to.toFile(); - } - } - } - else - { - reportFile = getRemoteFile(reportLocation,localFile); - return reportFile; - } - - } - catch (Exception e) { - throw new AdempiereException("Unknown exception: "+ e.getLocalizedMessage()); - } - } - - private String getRemoteMD5(String reportLocation) { - try { - String md5url = reportLocation + ".md5"; - URL reportURL = new URL(md5url); - try (InputStream in = reportURL.openStream()) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - byte buf[] = new byte[1024]; - int s = 0; - while((s = in.read(buf, 0, 1024)) > 0) - baos.write(buf, 0, s); + private WebResourceLoader webResourceLoader; + private AttachmentResourceLoader attachmentResourceLoader; + private ClassResourceLoader classResourceLoader; + private FileResourceLoader fileResourceLoader; + private BundleResourceLoader bundleResourceLoader; - String hash = new String(baos.toByteArray()); - int posSpace = hash.indexOf(" "); - if (posSpace > 0) - hash = hash.substring(0, posSpace); - return hash; - } - } catch (IOException e) { - if (log.isLoggable(Level.INFO)) - log.log(Level.INFO, "MD5 not available for " + reportLocation, e); - return null; - } - } - /** - * Returns the Server Connection if direct connection is not available - * (VPN, WAN, Terminal) and thus query has to be run on server only else return - * a direct connection to DB. - * - * Notes: Need to refactor and integrate in DB if confirmed to be working as - * expected. + * Returns read only connection for reporting * * @author Ashley Ramdass * @return Connection DB Connection @@ -363,7 +185,6 @@ public class ReportStarter implements ProcessCall, ClientProcess /** * Start the process. - * Called then pressing the Process button in R_Request. * It should only return false, if the function could not be performed * as this causes the process to abort. * @author rlemeill @@ -391,192 +212,129 @@ public class ReportStarter implements ProcessCall, ClientProcess private boolean startProcess0(Properties ctx, ProcessInfo pi, Trx trx) { processInfo = pi; - int nrows = 0; - Object onrows = null; - String Name=pi.getTitle(); + Object recordCounts = null; int AD_PInstance_ID=pi.getAD_PInstance_ID(); int Record_ID=pi.getRecord_ID(); - if (log.isLoggable(Level.INFO)) log.info( "Name="+Name+" AD_PInstance_ID="+AD_PInstance_ID+" Record_ID="+Record_ID); - String trxName = null; - if (trx != null) { - trxName = trx.getTrxName(); - } - ReportData reportData = getReportData(pi, trxName); - if (reportData == null) { - reportResult(AD_PInstance_ID, "Failed to retrieve report data", trxName); - pi.setSummary("Failed to retrieve report data", true); - return false; - } - - List jasperPrintList = new ArrayList(); - String reportFilePath = reportData.getReportFilePath(); - String[] reportPathList = reportFilePath.split(";"); - for (int idx = 0; idx < reportPathList.length; idx++) { - - String reportPath = reportPathList[idx]; - if (Util.isEmpty(reportPath, true)) - { - reportResult(AD_PInstance_ID, "Can not find report", trxName); - pi.setSummary("Can not find report", true); - return false; - } - if (reportPath.startsWith("@#LocalHttpAddr@")) { - String localaddr = Env.getContext(Env.getCtx(), Env.LOCAL_HTTP_ADDRESS); - if (!Util.isEmpty(localaddr)) { - reportPath = reportPath.replace("@#LocalHttpAddr@", localaddr); - } - } - - JasperData data = null; - File reportFile = null; - String fileExtension = ""; - HashMap params = new HashMap(); - - addProcessParameters(AD_PInstance_ID, params, trxName); - addProcessInfoParameters(params, pi.getParameter()); - - reportFile = getReportFile(reportPath, (String)params.get("ReportType")); - if (reportFile == null || reportFile.exists() == false) - { - log.severe("No report file found for given type, falling back to " + reportPath); - reportFile = getReportFile(reportPath); - } - - if (reportFile == null || reportFile.exists() == false) - { - String tmp = "Can not find report file at path - " + reportPath; - log.severe(tmp); - reportResult(AD_PInstance_ID, tmp, trxName); - pi.setSummary(tmp, true); - } - - if (reportFile != null) - { - data = processReport(reportFile); - fileExtension = reportFile.getName().substring(reportFile.getName().lastIndexOf("."), - reportFile.getName().length()); - } - else - { - return false; - } - - JasperReport jasperReport = data.getJasperReport(); - String jasperName = data.getJasperName(); - String name = jasperReport.getName(); - File reportDir = data.getReportDir(); - - String resourcePath = reportDir.getAbsolutePath(); - if (!resourcePath.endsWith("/") && !resourcePath.endsWith("\\")); - { - resourcePath = resourcePath + File.separator; - } - params.put("SUBREPORT_DIR", resourcePath); - if (reportPath.startsWith("http://") || reportPath.startsWith("https://")) { - int i = reportPath.lastIndexOf("/"); - String httpPath = reportPath.substring(0, i+1); - params.put("RESOURCE_DIR", httpPath); - } else { - params.put("RESOURCE_DIR", resourcePath); - } - - if (jasperReport != null && pi.getTable_ID() > 0 && Record_ID <= 0 && pi.getRecord_IDs() != null && pi.getRecord_IDs().size() > 0) - { - try - { - JRQuery originalQuery = jasperReport.getQuery(); - if (originalQuery != null) - { - String originalQueryText = originalQuery.getText(); - if (originalQueryText != null) - { - MTable table = new MTable(ctx, pi.getTable_ID(), trxName); - String tableName = table.getTableName(); - String originalQueryTemp = originalQueryText.toUpperCase(); - int index1 = originalQueryTemp.indexOf(" " + tableName.toUpperCase()); - if (index1 != -1) - { - int index2 = originalQueryTemp.substring(index1).indexOf(","); - if (index2 != -1) - { - String tableVariable = originalQueryTemp.substring(index1 + tableName.length() + 1, index1 + index2); - tableVariable = tableVariable.trim(); - - if (tableVariable.length() == 0) - tableVariable = tableName; - - MQuery query = new MQuery(tableName); - for (int recordId : pi.getRecord_IDs()) - query.addRestriction(tableVariable + "." + query.getTableName() + "_ID" + MQuery.EQUAL + recordId, false, 0); - - String newQueryText = null; - int index3 = originalQueryTemp.indexOf("WHERE"); - if (index3 != -1) - newQueryText = originalQueryText + " AND " + query.toString(); - else - newQueryText = originalQueryText + " WHERE " + query.toString(); - - File jrxmlFile = File.createTempFile(makePrefix(jasperReport.getName()), ".jrxml"); - JRXmlWriter.writeReport(jasperReport, new FileOutputStream(jrxmlFile), "UTF-8"); - - JasperDesign jasperDesign = JRXmlLoader.load(jrxmlFile); - - JRDesignQuery newQuery = new JRDesignQuery(); - newQuery.setText(newQueryText); - jasperDesign.setQuery(newQuery); - - JasperCompileManager manager = JasperCompileManager.getInstance(jasperReportStartContext); - JasperReport newJasperReport = manager.compile(jasperDesign); - if (newJasperReport != null) - { - data.jasperReport = newJasperReport; - jasperReport = newJasperReport; - } - } - } - } - } - } - catch(Exception e) - { - log.severe("Failed to modify the report query"); - } - } - - if (jasperReport != null) { - File[] subreports; + if (log.isLoggable(Level.INFO)) log.info( "Name="+pi.getTitle()+" AD_PInstance_ID="+AD_PInstance_ID+" Record_ID="+Record_ID); + String trxName = trx != null ? trx.getTrxName() : null; + ReportInfo reportInfo = getReportInfo(pi, trxName); + List jasperPrintList = new ArrayList(); + List batchPDFExportList = new ArrayList(); + List exportFileList = new ArrayList(); + PrintInfo printInfo = null; + String reportFilePath = reportInfo.getReportFilePath(); + String[] reportPathList = reportFilePath.split(";"); + for (String reportPath : reportPathList) { + if (Util.isEmpty(reportPath, true)) + { + pi.setSummary("Invalid report file path: " + reportFilePath, true); + return false; + } + if (reportPath.startsWith("@#LocalHttpAddr@")) { + String localaddr = Env.getContext(Env.getCtx(), Env.LOCAL_HTTP_ADDRESS); + if (!Util.isEmpty(localaddr)) { + reportPath = reportPath.replace("@#LocalHttpAddr@", localaddr); + } + } + + HashMap params = new HashMap(); + addProcessParameters(AD_PInstance_ID, params, trxName); + addProcessInfoParameters(params, pi.getParameter()); + + File reportFile = null; + URL reportURL = null; + Object reportObject = getReport(reportPath, (String)params.get("ReportType")); + if (reportObject == null) { + log.warning("No report file found for given type, falling back to " + reportPath); + reportObject = getReport(reportPath); + } + if (reportObject != null && reportObject instanceof File) { + reportFile = (File) reportObject; + if (reportFile.exists() == false) + { + reportFile = null; + } + + } else if (reportObject != null && reportObject instanceof URL) { + reportURL = (URL) reportObject; + } + + if (reportFile == null && reportURL == null) + { + String tmp = "Can not load report from path: " + reportPath; + pi.setSummary(tmp, true); + return false; + } + + JasperInfo jasperInfo = reportFile != null ? getJasperInfo(reportFile) : getJasperInfo(reportURL); + JasperReport jasperReport = jasperInfo.getJasperReport(); + String jasperName = jasperInfo.getJasperName(); + File reportDir = jasperInfo.getReportDir(); + + String fileResourcePath = null; + if (reportDir != null) { + fileResourcePath = reportDir.getAbsolutePath(); + if (!fileResourcePath.endsWith("/") && !fileResourcePath.endsWith("\\")); + { + fileResourcePath = fileResourcePath + File.separator; + } + } + if (WebResourceLoader.isWebResourcePath(reportPath)) { + String webPath = reportPath.substring(0, reportPath.lastIndexOf("/")+1); + params.put(SUBREPORT_DIR, webPath); + params.put(RESOURCE_DIR, webPath); + } else if (ClassResourceLoader.isClassResourcePath(reportPath)) { + params.put(SUBREPORT_DIR, getClassResourceLoader().getRelatedResourcesPath()); + params.put(RESOURCE_DIR, getClassResourceLoader().getRelatedResourcesPath()); + } else if (BundleResourceLoader.isBundleResourcePath(reportPath)) { + params.put(SUBREPORT_DIR, getBundleResourceLoader().getRelatedResourcesPath()); + params.put(RESOURCE_DIR, getBundleResourceLoader().getRelatedResourcesPath()); + } else { + params.put(SUBREPORT_DIR, fileResourcePath); + params.put(RESOURCE_DIR, fileResourcePath); + } + + if (pi.getTable_ID() > 0 && Record_ID <= 0 && pi.getRecord_IDs() != null && pi.getRecord_IDs().size() > 0) + { + jasperReport = processRecordIds(ctx, pi, trxName, jasperInfo, jasperReport); + } + + File[] subreports = null; // Subreports - if(reportPath.startsWith("http://") || reportPath.startsWith("https://")) + if(WebResourceLoader.isWebResourcePath(reportPath)) { - // Locate and download subreports from remote webcontext - subreports = getHttpSubreports(jasperName + "Subreport", reportPath, fileExtension); + subreports = new File[0]; } - else if (reportPath.startsWith("attachment:")) + else if (ClassResourceLoader.isClassResourcePath(reportPath)) { - subreports = getAttachmentSubreports(reportPath); + subreports = getClassResourceLoader().getSubreports(reportPath); } - else if (reportPath.startsWith("resource:")) + else if (BundleResourceLoader.isBundleResourcePath(reportPath)) { - String path = reportPath.substring(0, reportPath.length() +1 - (name+"."+fileExtension).length()); - subreports = getResourceSubreports(name+ "Subreport", path, fileExtension); + subreports = getBundleResourceLoader().getSubreports(reportPath); + } + else if (AttachmentResourceLoader.isAttachmentResourcePath(reportPath)) + { + subreports = getAttachmentResourceLoader().getSubreports(reportPath); } else { // Locate subreports from local/remote filesystem - subreports = reportDir.listFiles( new FileFilter( jasperName+"Subreport", reportDir, fileExtension)); + subreports = getFileResourceLoader().getSubreports(reportFile, reportDir); } + //legacy approach - ${JasperName} expression to access subreport + //doesn't work for web, class resource and bundle resource for( int i=0; i exporter = null; - - //JRExporter exporter = null; - if (ext.equals("pdf")) { - JRPdfExporter export = new JRPdfExporter(jasperReportStartContext); - SimplePdfExporterConfiguration config = new SimplePdfExporterConfiguration(); - export.setConfiguration(config); - export.setExporterOutput(new SimpleOutputStreamExporterOutput(strm)); - exporter = export; - // give a chance for customize jasper report configuration per report - JREventManage.sentPdfExporterConfigurationEvent(export, config, pi); - } else if (ext.equals("ps")) { - JRPrintServiceExporter export = new JRPrintServiceExporter( - jasperContext); - SimplePrintServiceExporterConfiguration config = new SimplePrintServiceExporterConfiguration(); - export.setConfiguration(config); - export.setExporterOutput(new SimpleOutputStreamExporterOutput(strm)); - exporter = export; - } else if (ext.equals("xml")) { - JRXmlExporter export = new JRXmlExporter(jasperContext); - SimpleExporterConfiguration config = new SimpleExporterConfiguration(); - export.setConfiguration(config); - export.setExporterOutput(new SimpleXmlExporterOutput(strm)); - exporter = export; - } else if (ext.equals("csv") || ext.equals("ssv") ) { - JRCsvExporter export = new JRCsvExporter(jasperContext); - SimpleCsvExporterConfiguration config = new SimpleCsvExporterConfiguration(); - if(ext.equals("ssv")) - config.setFieldDelimiter(";"); - export.setConfiguration(config); - export.setExporterOutput(new SimpleWriterExporterOutput(strm)); - exporter = export; - } else if (ext.equals("txt")) { - JRTextExporter export = new JRTextExporter(jasperContext); - SimpleTextExporterConfiguration config = new SimpleTextExporterConfiguration(); - export.setConfiguration(config); - export.setExporterOutput(new SimpleWriterExporterOutput(strm)); - exporter = export; - } else if (ext.equals("html") || ext.equals("htm")) { - HtmlExporter exporterHTML = new HtmlExporter(); - SimpleHtmlReportConfiguration htmlConfig = new SimpleHtmlReportConfiguration(); - htmlConfig.setEmbedImage(true); - htmlConfig.setAccessibleHtml(true); - exporterHTML.setExporterInput(SimpleExporterInput.getInstance(jasperPrintList)); - exporterHTML.setExporterOutput(new SimpleHtmlExporterOutput(file)); - exporterHTML.setConfiguration(htmlConfig); - exporter = exporterHTML; - } else if (ext.equals("xls")) { - JRXlsExporter exporterXLS = new JRXlsExporter(jasperContext); - SimpleXlsReportConfiguration xlsConfig = new SimpleXlsReportConfiguration(); - xlsConfig.setOnePagePerSheet(false); - exporterXLS.setExporterInput(SimpleExporterInput.getInstance(jasperPrintList)); - exporterXLS.setExporterOutput(new SimpleOutputStreamExporterOutput(strm)); - exporterXLS.setConfiguration(xlsConfig); - exporter = exporterXLS; - } else if (ext.equals("xlsx")) { - JRXlsxExporter exporterXLSX = new JRXlsxExporter(jasperContext); - SimpleXlsxReportConfiguration xlsxConfig = new SimpleXlsxReportConfiguration(); - xlsxConfig.setOnePagePerSheet(false); - exporterXLSX.setExporterInput(SimpleExporterInput.getInstance(jasperPrintList)); - exporterXLSX.setExporterOutput(new SimpleOutputStreamExporterOutput(strm)); - exporterXLSX.setConfiguration(xlsxConfig); - exporter = exporterXLSX; - } else { - log.severe("FileInvalidExtension="+ext); - strm.close(); - } - - if (exporter == null) - exporter = new JRPdfExporter(jasperReportStartContext); - - exporter.setExporterInput(new SimpleExporterInput(jasperPrint)); - - exporter.exportReport(); - processInfo.setExportFile(file); - } - catch (IOException e) - { - log.severe("ReportStarter.startProcess: Can not export PDF File - "+ e.getMessage()); - } + doExport(pi, jasperPrint, exportFileList); } } catch (JRException e) { throw new AdempiereException(e.getLocalizedMessage() + (e.getCause() != null ? " -> " + e.getCause().getLocalizedMessage() : "")); } finally { - if (conn != null) { + if (trx == null && conn != null) { try { conn.close(); } catch (SQLException e) { } } } - } + } // for reportPathList - } // for reportPathList - - if (onrows != null && onrows instanceof Integer) { - nrows = (Integer) onrows; - processInfo.setRowCount(nrows); + if (batchPDFExportList.size() > 0) { + if (batchPDFExportList.size() == 1) { + processInfo.setPDFReport(batchPDFExportList.get(0)); + } else { + try { + File pdfFile = File.createTempFile(makePrefix(processInfo.getTitle()), ".pdf"); + Util.mergePdf(batchPDFExportList, pdfFile); + processInfo.setPDFReport(pdfFile); + } catch (Exception e) { + throw new AdempiereException(e.getMessage(), e); + } + } + } else if (exportFileList.size() > 0) { + if (exportFileList.size() == 1) { + processInfo.setExportFile(exportFileList.get(0)); + } else { + try { + processInfo.setExportFile(createMultiFileArchive(exportFileList)); + } catch (Exception e) { + throw new AdempiereException(e.getMessage(), e); + } + } + } else { + if (jasperPrintList.size() == 1) { + JRViewerProvider viewerLauncher = getViewerProvider(); + JasperPrint jasperPrint = jasperPrintList.get(0); + if (!Util.isEmpty(processInfo.getReportType())) { + jasperPrint.setProperty(IDEMPIERE_REPORT_TYPE, processInfo.getReportType()); + } + try { + viewerLauncher.openViewer(jasperPrint, pi.getTitle(), printInfo); + } catch (JRException e) { + throw new AdempiereException(e.getLocalizedMessage() + (e.getCause() != null ? " -> " + e.getCause().getLocalizedMessage() : ""), e); + } + } else { + JRViewerProviderList viewerLauncher = getViewerProviderList(); + if (viewerLauncher == null) { + throw new AdempiereException("Can not find a viewer provider for multiple jasper reports"); + } + try { + viewerLauncher.openViewer(jasperPrintList, pi.getTitle(), printInfo); + } catch (JRException e) { + throw new AdempiereException(e.getLocalizedMessage() + (e.getCause() != null ? " -> " + e.getCause().getLocalizedMessage() : ""), e); + } + } + } + + if (recordCounts != null && recordCounts instanceof Integer) { + processInfo.setRowCount((Integer) recordCounts); } - reportResult( AD_PInstance_ID, null, trxName); pi.setSummary(Msg.getMsg(Env.getCtx(), "Success"), false); return true; } + private File createMultiFileArchive(List exportFileList) throws Exception { + File archiveFile = File.createTempFile(makePrefix(processInfo.getTitle()), ".zip"); + try (FileOutputStream out = new FileOutputStream(archiveFile)) { + try (ZipOutputStream zip = new ZipOutputStream(out);) { + zip.setMethod(ZipOutputStream.DEFLATED); + zip.setLevel(Deflater.BEST_COMPRESSION); + // + for (File file : exportFileList) { + try { + ZipEntry entry = new ZipEntry(file.getName()); + entry.setTime(System.currentTimeMillis()); + entry.setMethod(ZipEntry.DEFLATED); + zip.putNextEntry(entry); + try (InputStream in = new FileInputStream(file)) { + byte buf[] = new byte[1024]; + int s = 0; + while ((s = in.read(buf, 0, 1024)) > 0) + zip.write(buf, 0, s); + } + zip.closeEntry(); + } catch (Exception e) { + log.log(Level.SEVERE, e.getMessage(), e); + } + } + } + } + return archiveFile; + } + + private void doBatchExport(JasperPrint jasperPrint, List batchExportList) throws JRException { + try + { + File pdfFile = null; + if (processInfo.getPDFFileName() != null) { + pdfFile = new File(processInfo.getPDFFileName()); + } else { + pdfFile = File.createTempFile(makePrefix(jasperPrint.getName()), ".pdf"); + } + + JRPdfExporter exporter = new JRPdfExporter(jasperReportContext); + exporter.setExporterInput(new SimpleExporterInput(jasperPrint)); + exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(pdfFile.getAbsolutePath())); + exporter.exportReport(); + batchExportList.add(pdfFile); + } + catch (IOException e) + { + log.log(Level.SEVERE, "ReportStarter.startProcess: Can not make PDF File - "+ e.getMessage(), e); + } + } + + /** + * Add process info record ids to report query (multiple or) + * @param ctx + * @param pi + * @param trxName + * @param jasperData + * @param jasperReport + * @return JasReport + */ + private JasperReport processRecordIds(Properties ctx, ProcessInfo pi, String trxName, JasperInfo jasperData, + JasperReport jasperReport) { + try + { + JRQuery originalQuery = jasperReport.getQuery(); + if (originalQuery != null) + { + String originalQueryText = originalQuery.getText(); + if (originalQueryText != null) + { + MTable table = new MTable(ctx, pi.getTable_ID(), trxName); + String tableName = table.getTableName(); + String originalQueryTemp = originalQueryText.toUpperCase(); + int index1 = originalQueryTemp.indexOf(" " + tableName.toUpperCase()); + if (index1 != -1) + { + int index2 = originalQueryTemp.substring(index1).indexOf(","); + if (index2 != -1) + { + String tableVariable = originalQueryTemp.substring(index1 + tableName.length() + 1, index1 + index2); + tableVariable = tableVariable.trim(); + + if (tableVariable.length() == 0) + tableVariable = tableName; + + MQuery query = new MQuery(tableName); + for (int recordId : pi.getRecord_IDs()) + query.addRestriction(tableVariable + "." + query.getTableName() + "_ID" + MQuery.EQUAL + recordId, false, 0); + + String newQueryText = null; + int index3 = originalQueryTemp.indexOf("WHERE"); + if (index3 != -1) + newQueryText = originalQueryText + " AND " + query.toString(); + else + newQueryText = originalQueryText + " WHERE " + query.toString(); + + File jrxmlFile = File.createTempFile(makePrefix(jasperReport.getName()), ".jrxml"); + JRXmlWriter.writeReport(jasperReport, new FileOutputStream(jrxmlFile), "UTF-8"); + + JasperDesign jasperDesign = JRXmlLoader.load(jrxmlFile); + + JRDesignQuery newQuery = new JRDesignQuery(); + newQuery.setText(newQueryText); + jasperDesign.setQuery(newQuery); + + JasperCompileManager manager = JasperCompileManager.getInstance(jasperReportContext); + JasperReport newJasperReport = manager.compile(jasperDesign); + if (newJasperReport != null) + { + jasperData.jasperReport = newJasperReport; + jasperReport = newJasperReport; + } + } + } + } + } + } + catch(Exception e) + { + log.log(Level.SEVERE, "Failed to modify the report query", e); + } + return jasperReport; + } + + private void doDirectPrint(ProcessInfo pi, String printerName, MPrintFormat printFormat, PrintInfo printInfo, + JasperPrint jasperPrint) throws JRException { + // Get printer job + PrinterJob printerJob = PrintUtil.getPrinterJob(printerName); + // Set print request attributes + + // Paper Attributes: + PrintRequestAttributeSet prats = new HashPrintRequestAttributeSet(); + + // add: copies, job-name, priority + if (printInfo == null || printInfo.isDocumentCopy() || printInfo.getCopies() < 1) // @Trifon + prats.add (new Copies(1)); + else + prats.add (new Copies(printInfo.getCopies())); + Locale locale = Language.getLoginLanguage().getLocale(); + // @Trifon + String printFormat_name = printFormat == null ? "" : printFormat.getName(); + int numCopies = printInfo == null ? 0 : printInfo.getCopies(); + prats.add(new JobName(printFormat_name + "_" + pi.getRecord_ID(), locale)); + prats.add(PrintUtil.getJobPriority(jasperPrint.getPages().size(), numCopies, true)); + + // Create print service exporter + JRPrintServiceExporter exporter = new JRPrintServiceExporter(); + // Set parameters + exporter.setExporterInput(new SimpleExporterInput(jasperPrint)); + SimplePrintServiceExporterConfiguration configuration = new SimplePrintServiceExporterConfiguration(); + configuration.setPrintService(printerJob.getPrintService()); + configuration.setPrintServiceAttributeSet(printerJob.getPrintService().getAttributes()); + configuration.setPrintRequestAttributeSet(prats); + configuration.setDisplayPageDialog(false); + configuration.setDisplayPrintDialog(false); + exporter.setConfiguration(configuration); + // Print report / document + exporter.exportReport(); + } + + private void doExport(ProcessInfo pi, JasperPrint jasperPrint, List exportFileList) throws JRException { + String ext = pi.getExportFileExtension(); + if (ext == null) + ext = "pdf"; + try { + File exportFile = File.createTempFile(makePrefix(jasperPrint.getName()), "." + ext); + + try (FileOutputStream outputStream = new FileOutputStream(exportFile);) { + + Exporter exporter = null; + if (ext.equals("pdf")) { + JRPdfExporter export = new JRPdfExporter(jasperReportContext); + SimplePdfExporterConfiguration config = new SimplePdfExporterConfiguration(); + export.setConfiguration(config); + export.setExporterOutput(new SimpleOutputStreamExporterOutput(outputStream)); + exporter = export; + // give a chance for customize jasper report configuration per report + JREventManage.sentPdfExporterConfigurationEvent(export, config, pi); + } else if (ext.equals("ps")) { + JRPrintServiceExporter export = new JRPrintServiceExporter(jasperReportContext); + SimplePrintServiceExporterConfiguration config = new SimplePrintServiceExporterConfiguration(); + export.setConfiguration(config); + export.setExporterOutput(new SimpleOutputStreamExporterOutput(outputStream)); + exporter = export; + } else if (ext.equals("xml")) { + JRXmlExporter export = new JRXmlExporter(jasperReportContext); + SimpleExporterConfiguration config = new SimpleExporterConfiguration(); + export.setConfiguration(config); + export.setExporterOutput(new SimpleXmlExporterOutput(outputStream)); + exporter = export; + } else if (ext.equals("csv") || ext.equals("ssv") ) { + JRCsvExporter export = new JRCsvExporter(jasperReportContext); + SimpleCsvExporterConfiguration config = new SimpleCsvExporterConfiguration(); + if(ext.equals("ssv")) + config.setFieldDelimiter(";"); + export.setConfiguration(config); + export.setExporterOutput(new SimpleWriterExporterOutput(outputStream)); + exporter = export; + } else if (ext.equals("txt")) { + JRTextExporter export = new JRTextExporter(jasperReportContext); + SimpleTextExporterConfiguration config = new SimpleTextExporterConfiguration(); + export.setConfiguration(config); + export.setExporterOutput(new SimpleWriterExporterOutput(outputStream)); + exporter = export; + } else if (ext.equals("html") || ext.equals("htm")) { + HtmlExporter exporterHTML = new HtmlExporter(); + SimpleHtmlReportConfiguration htmlConfig = new SimpleHtmlReportConfiguration(); + htmlConfig.setEmbedImage(true); + htmlConfig.setAccessibleHtml(true); + exporterHTML.setExporterOutput(new SimpleHtmlExporterOutput(outputStream)); + exporterHTML.setConfiguration(htmlConfig); + exporter = exporterHTML; + } else if (ext.equals("xls")) { + JRXlsExporter exporterXLS = new JRXlsExporter(jasperReportContext); + SimpleXlsReportConfiguration xlsConfig = new SimpleXlsReportConfiguration(); + xlsConfig.setOnePagePerSheet(false); + exporterXLS.setExporterOutput(new SimpleOutputStreamExporterOutput(outputStream)); + exporterXLS.setConfiguration(xlsConfig); + exporter = exporterXLS; + } else if (ext.equals("xlsx")) { + JRXlsxExporter exporterXLSX = new JRXlsxExporter(jasperReportContext); + SimpleXlsxReportConfiguration xlsxConfig = new SimpleXlsxReportConfiguration(); + xlsxConfig.setOnePagePerSheet(false); + exporterXLSX.setExporterOutput(new SimpleOutputStreamExporterOutput(outputStream)); + exporterXLSX.setConfiguration(xlsxConfig); + exporter = exporterXLSX; + } else { + log.warning("FileInvalidExtension="+ext); + } + + if (exporter == null) + exporter = new JRPdfExporter(jasperReportContext); + + exporter.setExporterInput(new SimpleExporterInput(jasperPrint)); + + exporter.exportReport(); + exportFileList.add(exportFile); + } + } catch (IOException e) { + log.log(Level.SEVERE, "Can not export " + ext + " File - "+ e.getMessage(), e); + } + } + private static IServiceReferenceHolder s_viewerProviderListReference = null; /** @@ -948,183 +829,43 @@ public class ReportStarter implements ProcessCall, ClientProcess return prefix.toString(); } - /** - * Get .property resource file from process attachment - * @param bundleName - * @param currLang - * @return File - */ - private File getAttachmentResourceFile(String bundleName, Language currLang) { - String resname = bundleName+"_"+currLang.getLocale().getLanguage()+"_"+currLang.getLocale().getCountry()+".properties"; - File resFile = getAttachmentEntryFile(resname); - if (resFile == null) { - resname = bundleName+"_"+currLang.getLocale().getLanguage()+".properties"; - resFile = getAttachmentEntryFile(resname); - if (resFile == null) { - resname = bundleName+".properties"; - resFile = getAttachmentEntryFile(resname); - } - } - return resFile; + private WebResourceLoader getWebResourceLoader() { + if (webResourceLoader == null) + webResourceLoader = new WebResourceLoader(getLocalDownloadFolder()); + return webResourceLoader; + } + + private AttachmentResourceLoader getAttachmentResourceLoader() { + if (attachmentResourceLoader == null) + attachmentResourceLoader = new AttachmentResourceLoader(getLocalDownloadFolder()); + return attachmentResourceLoader; } - private File getAttachmentEntryFile(String resname) { - File fileattach = null; - MAttachmentEntry[] entries = attachment.getEntries(); - for( int i=0; i subreports = new ArrayList(); - MAttachmentEntry[] entries = attachment.getEntries(); - for(int i = 0; i < entries.length; i++) { - // @Trifon - if (!entries[i].getName().equals(name)) - { - File reportFile = getAttachmentEntryFile(entries[i]); - if (reportFile != null) { - if (entries[i].getName().toLowerCase().endsWith(".jrxml") - || entries[i].getName().toLowerCase().endsWith(".jasper")) { - subreports.add(reportFile); - } - } - } - } - File[] subreportsTemp = new File[0]; - subreportsTemp = subreports.toArray(subreportsTemp); - return subreportsTemp; - } - - /** - * Search for additional subreports deployed as resources - * @param reportName The original report name - * @param reportPath The full path to the parent report - * @param fileExtension The file extension of the parent report - * @return An Array of File objects referencing to the downloaded subreports - */ - private File[] getResourceSubreports(String reportName, String reportPath, String fileExtension) - { - ArrayList subreports = new ArrayList(); - // Currently check hardcoded for max. 10 subreports - for(int i=1; i<10; i++) - { - // Check if subreport number i exists - File subreport = null; - try { - subreport = getFileAsResource(reportPath + reportName + i + fileExtension); - } catch (Exception e) { - // just ignore it - } - if(subreport == null) // Subreport doesn't exist, abort further approaches - break; - - subreports.add(subreport); - } - - File[] subreportsTemp = new File[subreports.size()]; - subreportsTemp = subreports.toArray(subreportsTemp); - return subreportsTemp; - } - + /** * @author alinv * @param reportPath - * @param reportType - * @return the abstract file corresponding to typed report + * @param reportType optional postfix parameter to select a different jasper report file + * @return File or URL */ - protected File getReportFile(String reportPath, String reportType) { + protected Object getReport(String reportPath, String reportType) { if (reportType != null) { @@ -1132,152 +873,39 @@ public class ReportStarter implements ProcessCall, ClientProcess reportPath = reportPath.substring(0, cpos) + "_" + reportType + reportPath.substring(cpos, reportPath.length()); } - return getReportFile(reportPath); + return getReport(reportPath); } /** * @author alinv * @param reportPath - * @return the abstract file corresponding to report + * @return File or URL */ - protected File getReportFile(String reportPath) + protected Object getReport(String reportPath) { - File reportFile = null; + Object report = null; // Reports deployment on web server Thanks to Alin Vaida - if (reportPath.startsWith("http://") || reportPath.startsWith("https://")) { - reportFile = httpDownloadedReport(reportPath); - } else if (reportPath.startsWith("attachment:")) { + if (WebResourceLoader.isWebResourcePath(reportPath)) { + report = getWebResourceLoader().getReportFile(reportPath); + } else if (AttachmentResourceLoader.isAttachmentResourcePath(reportPath)) { //report file from process attachment - reportFile = downloadAttachment(reportPath); + report = getAttachmentResourceLoader().getReportFile(processInfo, reportPath); } else if (reportPath.startsWith("/")) { - reportFile = new File(reportPath); - } else if (reportPath.startsWith("file:/")) { - try { - reportFile = new File(new URI(reportPath)); - } catch (URISyntaxException e) { - log.warning(e.getLocalizedMessage()); - reportFile = null; - } - } else if (reportPath.startsWith("resource:")) { - try { - reportFile = getFileAsResource(reportPath); - } catch (Exception e) { - log.warning(e.getLocalizedMessage()); - reportFile = null; - } + report = new File(reportPath); + } else if (FileResourceLoader.isFileResourcePath(reportPath)) { + report = getFileResourceLoader().getReportFile(reportPath); + } else if (ClassResourceLoader.isClassResourcePath(reportPath)) { + report = getClassResourceLoader().getResource(reportPath); + } else if (BundleResourceLoader.isBundleResourcePath(reportPath)) { + report = getBundleResourceLoader().getResource(reportPath); } else { - reportFile = new File(REPORT_HOME, reportPath); + report = new File(REPORT_HOME, reportPath); } - return reportFile; + return report; } - - /** - * @param reportPath - * @return - * @throws Exception - */ - private File getFileAsResource(String reportPath) throws Exception { - File reportFile; - String name = reportPath.substring("resource:".length()).trim(); - String localName = name.replace('/', '_'); - if (log.isLoggable(Level.INFO)) { - log.info("reportPath = " + reportPath); - log.info("getting resource from = " + getClass().getClassLoader().getResource(name)); - } - InputStream inputStream = getClass().getClassLoader().getResourceAsStream(name); - String localFile = getLocalDownloadFolder() + localName; - if (log.isLoggable(Level.INFO)) log.info("localFile = " + localFile); - reportFile = new File(localFile); - - boolean empty = true; - OutputStream out = null; - try { - out = new FileOutputStream(reportFile); - if (out != null){ - byte buf[]=new byte[1024]; - int len; - while((len=inputStream.read(buf))>0) { - empty = false; - out.write(buf,0,len); - } - } - } catch (Exception e) { - throw e; - } finally { - if (out != null) - out.close(); - if (inputStream != null) - inputStream.close(); - } - - if (empty) - return null; - else - return reportFile; - } - - /** - * Download db attachment - * @param reportPath must of syntax attachment:filename - * @return File - */ - private File downloadAttachment(String reportPath) { - File reportFile = null; - String name = reportPath.substring("attachment:".length()).trim(); - MProcess process = new MProcess(Env.getCtx(), processInfo.getAD_Process_ID(), processInfo.getTransactionName()); - attachment = process.getAttachment(); - if (attachment != null) { - MAttachmentEntry[] entries = attachment.getEntries(); - MAttachmentEntry entry = null; - for (int i = 0; i < entries.length; i++) { - if (entries[i].getName().equals(name)) { - entry = entries[i]; - break; - } - } - if (entry != null) { - reportFile = getAttachmentEntryFile(entry); - } - } - return reportFile; - } - - /** - * Download db attachment to local file - * @param entry - * @return File - */ - private File getAttachmentEntryFile(MAttachmentEntry entry) { - String localFile = getLocalDownloadFolder() + entry.getName(); - String downloadedLocalFile = getLocalDownloadFolder()+"TMP_" + entry.getName(); - File reportFile = new File(localFile); - if (reportFile.exists()) { - String localMD5hash = DigestOfFile.getMD5Hash(reportFile); - String entryMD5hash = DigestOfFile.getMD5Hash(entry.getData()); - if (localMD5hash.equals(entryMD5hash)) - { - log.info(" no need to download: local report is up-to-date"); - } - else - { - log.info(" report on server is different that local one, download and replace"); - File downloadedFile = new File(downloadedLocalFile); - entry.getFile(downloadedFile); - if (! reportFile.delete()) { - throw new AdempiereException("Cannot delete temporary file " + reportFile.toString()); - } - if (! downloadedFile.renameTo(reportFile)) { - throw new AdempiereException("Cannot rename temporary file " + downloadedFile.toString() + " to " + reportFile.toString()); - } - } - } else { - entry.getFile(reportFile); - } - return reportFile; - } - + private String getLocalDownloadFolder() { String path = System.getProperty("java.io.tmpdir") + System.getProperty("file.separator") + "jasperreport_"+processInfo.getAD_Process_ID() + System.getProperty("file.separator"); @@ -1291,80 +919,91 @@ public class ReportStarter implements ProcessCall, ClientProcess return path; } - /** - * Update AD_PInstance result and error message - * @author rlemeill - * @param AD_PInstance_ID - * @param errMsg error message or null if there is no error - */ - protected void reportResult(int AD_PInstance_ID, String errMsg, String trxName) - { - int result = (errMsg == null ? 1 : 0); - String sql = "UPDATE AD_PInstance SET Result=?, ErrorMsg=?" - +" WHERE AD_PInstance_ID="+AD_PInstance_ID; - DB.executeUpdateEx(sql, new Object[]{result, errMsg}, trxName); - } - /** + * Process/Compile report file * @author rlemeill * @param reportFile - * @return + * @return JasperInfo */ - protected JasperData processReport( File reportFile) { + protected JasperInfo getJasperInfo( File reportFile) { if (log.isLoggable(Level.INFO)) log.info( "reportFile.getAbsolutePath() = "+reportFile.getAbsolutePath()); JasperReport jasperReport = null; + String extension = null; String jasperName = reportFile.getName(); - int pos = jasperName.indexOf('.'); - if (pos!=-1) jasperName = jasperName.substring(0, pos); + int pos = jasperName.lastIndexOf('.'); + if (pos!=-1) { + extension = jasperName.substring(pos); + jasperName = jasperName.substring(0, pos); + } File reportDir = reportFile.getParentFile(); - - //test if the compiled report exists - File jasperFile = new File( reportDir.getAbsolutePath(), jasperName+".jasper"); - if (jasperFile.exists()) { // test time - if (reportFile.lastModified() == jasperFile.lastModified()) { - if (log.isLoggable(Level.INFO)) log.info(" no need to compile use "+jasperFile.getAbsolutePath()); - try { - jasperReport = (JasperReport)JRLoader.loadObjectFromFile(jasperFile.getAbsolutePath()); - } catch (JRException e) { - jasperReport = null; - log.severe("Can not load report - "+ e.getMessage()); - } - } else { - jasperReport = compileReport( reportFile, jasperFile); - } - } else { // create new jasper file - jasperReport = compileReport( reportFile, jasperFile); + File jasperFile = null; + if (extension == null || !extension.equals(".jasper" )) { + //test if the compiled report exists + jasperFile = new File( reportDir.getAbsolutePath(), jasperName+".jasper"); + if (jasperFile.exists()) { // test time + if (reportFile.lastModified() == jasperFile.lastModified()) { + if (log.isLoggable(Level.INFO)) log.info(" no need to compile use "+jasperFile.getAbsolutePath()); + try { + jasperReport = (JasperReport)JRLoader.loadObjectFromFile(jasperFile.getAbsolutePath()); + } catch (JRException e) { + jasperReport = null; + log.log(Level.SEVERE, "Can not load report - "+ e.getMessage(), e); + } + } else { + jasperReport = compileReport( reportFile, jasperFile); + } + } else { // create new jasper file + jasperReport = compileReport( reportFile, jasperFile); + } + } else { + jasperFile = reportFile; + try { + jasperReport = (JasperReport)JRLoader.loadObjectFromFile(jasperFile.getAbsolutePath()); + } catch (JRException e) { + jasperReport = null; + log.log(Level.SEVERE, "Can not load report - "+ e.getMessage(), e); + } } - return new JasperData( jasperReport, reportDir, jasperName, jasperFile); + return new JasperInfo( jasperReport, reportDir, jasperName, jasperFile); } + protected JasperInfo getJasperInfo(URL reportURL) { + try { + JasperReport jasperReport = (JasperReport)JRLoader.loadObject(reportURL); + return new JasperInfo( jasperReport, null, jasperReport.getName(), null); + } catch (JRException e) { + log.log(Level.SEVERE, e.getMessage(), e); + } + return null; + } + /** * Load Process Parameters into given params map * @param AD_PInstance_ID * @param params * @param trxName */ - private static void addProcessParameters(int AD_PInstance_ID, Map params, String trxName) + private void addProcessParameters(int AD_PInstance_ID, Map params, String trxName) { - final String sql = "SELECT " - +" "+X_AD_PInstance_Para.COLUMNNAME_ParameterName - +","+X_AD_PInstance_Para.COLUMNNAME_P_String - +","+X_AD_PInstance_Para.COLUMNNAME_P_String_To - +","+X_AD_PInstance_Para.COLUMNNAME_P_Number - +","+X_AD_PInstance_Para.COLUMNNAME_P_Number_To - +","+X_AD_PInstance_Para.COLUMNNAME_P_Date - +","+X_AD_PInstance_Para.COLUMNNAME_P_Date_To - +","+X_AD_PInstance_Para.COLUMNNAME_Info - +","+X_AD_PInstance_Para.COLUMNNAME_Info_To - +" FROM "+X_AD_PInstance_Para.Table_Name - +" WHERE "+X_AD_PInstance_Para.COLUMNNAME_AD_PInstance_ID+"=?"; + final StringBuilder sql = new StringBuilder("SELECT ") + .append(" ").append(X_AD_PInstance_Para.COLUMNNAME_ParameterName) + .append(",").append(X_AD_PInstance_Para.COLUMNNAME_P_String) + .append(",").append(X_AD_PInstance_Para.COLUMNNAME_P_String_To) + .append(",").append(X_AD_PInstance_Para.COLUMNNAME_P_Number) + .append(",").append(X_AD_PInstance_Para.COLUMNNAME_P_Number_To) + .append(",").append(X_AD_PInstance_Para.COLUMNNAME_P_Date) + .append(",").append(X_AD_PInstance_Para.COLUMNNAME_P_Date_To) + .append(",").append(X_AD_PInstance_Para.COLUMNNAME_Info) + .append(",").append(X_AD_PInstance_Para.COLUMNNAME_Info_To) + .append(" FROM ").append(X_AD_PInstance_Para.Table_Name) + .append(" WHERE ").append(X_AD_PInstance_Para.COLUMNNAME_AD_PInstance_ID+"=?"); PreparedStatement pstmt = null; ResultSet rs = null; try { - pstmt = DB.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, trxName); + pstmt = DB.prepareStatement(sql.toString(), ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, trxName); pstmt.setInt(1, AD_PInstance_ID); rs = pstmt.executeQuery(); while (rs.next()) @@ -1418,7 +1057,7 @@ public class ReportStarter implements ProcessCall, ClientProcess } catch (SQLException e) { - throw new DBException(e, sql); + throw new DBException(e, sql.toString()); } finally { @@ -1465,7 +1104,7 @@ public class ReportStarter implements ProcessCall, ClientProcess { JasperReport compiledJasperReport = null; try { - JasperCompileManager manager = JasperCompileManager.getInstance(jasperReportStartContext); + JasperCompileManager manager = JasperCompileManager.getInstance(jasperReportContext); manager.compileToFile(reportFile.getAbsolutePath(), jasperFile.getAbsolutePath() ); jasperFile.setLastModified( reportFile.lastModified()); //Synchronize Dates compiledJasperReport = (JasperReport)JRLoader.loadObject(jasperFile); @@ -1478,22 +1117,22 @@ public class ReportStarter implements ProcessCall, ClientProcess /** * @author rlemeill * @param ProcessInfo - * @return ReportData or null if no data found + * @return ReportInfo */ - public ReportData getReportData (ProcessInfo pi, String trxName) + private ReportInfo getReportInfo (ProcessInfo pi, String trxName) { MProcess process = MProcess.get(pi.getAD_Process_ID()); String path = process.getJasperReport(); boolean isPrintPreview = pi.isPrintPreview(); boolean directPrint = (process.isDirectPrint() && !Ini.isPropertyBool(Ini.P_PRINTPREVIEW) && !isPrintPreview); - return new ReportData( path, directPrint); + return new ReportInfo( path, directPrint); } - static class ReportData { + private static class ReportInfo { private String reportFilePath; private boolean directPrint; - public ReportData(String reportFilePath, boolean directPrint) { + public ReportInfo(String reportFilePath, boolean directPrint) { this.reportFilePath = reportFilePath; this.directPrint = directPrint; } @@ -1507,19 +1146,18 @@ public class ReportStarter implements ProcessCall, ClientProcess } } - public static class JasperData - implements Serializable + private static class JasperInfo implements Serializable { /** - * + * generated serial id */ - private static final long serialVersionUID = 4375195020654531202L; + private static final long serialVersionUID = -1208951101124159422L; private JasperReport jasperReport; private File reportDir; private String jasperName; private File jasperFile; - public JasperData(JasperReport jasperReport, File reportDir, String jasperName, File jasperFile) { + public JasperInfo(JasperReport jasperReport, File reportDir, String jasperName, File jasperFile) { this.jasperReport = jasperReport; this.reportDir = reportDir; this.jasperName = jasperName; @@ -1543,28 +1181,6 @@ public class ReportStarter implements ProcessCall, ClientProcess } } - static class FileFilter implements FilenameFilter { - private String reportStart; - private File directory; - private String extension; - - public FileFilter(String reportStart, File directory, String extension) { - this.reportStart = reportStart; - this.directory = directory; - this.extension = extension; - } - - public boolean accept(File file, String name) { - if (file.equals( directory)) { - if (name.startsWith( reportStart)) { - int pos = name.lastIndexOf( extension); - if ( (pos!=-1) && (pos==(name.length()-extension.length()))) return true; - } - } - return false; - } - } - @Override public void setProcessUI(IProcessUI processUI) { m_processUI = processUI; diff --git a/org.adempiere.report.jasper/src/org/adempiere/report/jasper/WebResourceLoader.java b/org.adempiere.report.jasper/src/org/adempiere/report/jasper/WebResourceLoader.java new file mode 100644 index 0000000000..9b198c54af --- /dev/null +++ b/org.adempiere.report.jasper/src/org/adempiere/report/jasper/WebResourceLoader.java @@ -0,0 +1,241 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * 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., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + * Contributors: * + * - hengsin * + **********************************************************************/ +package org.adempiere.report.jasper; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.logging.Level; + +import org.adempiere.exceptions.AdempiereException; +import org.compiere.util.CLogger; +import org.compiere.util.Language; +import org.compiere.util.Util; +import org.compiere.utils.DigestOfFile; + +/** + * + * Load report resources from http or https + * @author hengsin + * + */ +public class WebResourceLoader { + + private final static CLogger log = CLogger.getCLogger(WebResourceLoader.class); + + private String destinationFolder; + + /** + * + * @param destinationFolder destination folder to download report file to + */ + public WebResourceLoader(String destinationFolder) { + this.destinationFolder = destinationFolder; + } + + /** + * Download remote report file from web server + * + * @param reportFilePath full http path to report file + * @return downloaded File + */ + private File downloadFile(String reportFilePath) { + File reportFile = null; + File downloadedFile = null; + if (log.isLoggable(Level.INFO)) + log.info(" report deployed to " + reportFilePath); + try { + String[] tmps = reportFilePath.split("/"); + String fileName = tmps[tmps.length - 1]; + String localFilePath = destinationFolder + fileName; + String tmpLocalFilePath = destinationFolder + "TMP_" + fileName; + + reportFile = new File(localFilePath); + if (reportFile.exists()) { + String remoteMD5Hash = getRemoteMD5(reportFilePath); + if (!Util.isEmpty(remoteMD5Hash, true)) { + String localMD5hash = DigestOfFile.getMD5Hash(reportFile); + if (log.isLoggable(Level.INFO)) + log.info("MD5 for local file is " + localMD5hash); + if (localMD5hash.equals(remoteMD5Hash.trim())) { + if (log.isLoggable(Level.INFO)) + log.info("MD5 match: local report file is up-to-date"); + return reportFile; + } else { + if (log.isLoggable(Level.INFO)) + log.info("MD5 is different, download and replace"); + downloadedFile = getRemoteFile(reportFilePath, tmpLocalFilePath); + if (downloadedFile != null) { + Path to = reportFile.toPath(); + Path from = downloadedFile.toPath(); + Files.copy(from, to, StandardCopyOption.REPLACE_EXISTING); + Files.delete(from); + return to.toFile(); + } else { + return null; + } + } + } else { + downloadedFile = getRemoteFile(reportFilePath, tmpLocalFilePath); + if (downloadedFile == null) + return null; + + // compare hash of existing and downloaded + if (DigestOfFile.md5HashCompare(reportFile, downloadedFile)) { + // nothing file are identical + if (log.isLoggable(Level.INFO)) + log.info("MD5 match: local report file is up-to-date"); + return reportFile; + } else { + if (log.isLoggable(Level.INFO)) + log.info("MD5 is different, replace with downloaded file"); + Path to = reportFile.toPath(); + Path from = downloadedFile.toPath(); + Files.copy(from, to, StandardCopyOption.REPLACE_EXISTING); + Files.delete(from); + return to.toFile(); + } + } + } else { + reportFile = getRemoteFile(reportFilePath, localFilePath); + return reportFile; + } + + } catch (Exception e) { + throw new AdempiereException("Unknown exception: " + e.getLocalizedMessage()); + } + + } + + /** + * + * @param reportFilePath + * @return File + */ + public File getReportFile(String reportFilePath) { + return downloadFile(reportFilePath); + } + + /** + * Get property resource file from http URL + * + * @param reportPath + * @param bundleName + * @param currLang + * @return File + */ + public File getResourceBundle(String reportPath, String bundleName, Language currLang) { + String remoteDir = reportPath.substring(0, reportPath.lastIndexOf("/")); + String resname = bundleName + "_" + currLang.getLocale().getLanguage() + "_" + currLang.getLocale().getCountry() + + ".properties"; + try { + File resFile = downloadFile(remoteDir + "/" + resname); + if (resFile == null) { + resname = bundleName + "_" + currLang.getLocale().getLanguage() + ".properties"; + resFile = downloadFile(remoteDir + "/" + resname); + if (resFile == null) { + resname = bundleName + ".properties"; + resFile = downloadFile(remoteDir + "/" + resname); + } + } + return resFile; + } catch (Exception e) { + if (log.isLoggable(Level.CONFIG)) + log.log(Level.CONFIG, e.getMessage(), e); + return null; + } + } + + private String getRemoteMD5(String reportLocation) { + try { + String md5url = reportLocation + ".md5"; + URL reportURL = new URL(md5url); + try (InputStream in = reportURL.openStream()) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte buf[] = new byte[1024]; + int s = 0; + while ((s = in.read(buf, 0, 1024)) > 0) + baos.write(buf, 0, s); + + String hash = new String(baos.toByteArray()); + int posSpace = hash.indexOf(" "); + if (posSpace > 0) + hash = hash.substring(0, posSpace); + return hash; + } + } catch (Exception e) { + if (log.isLoggable(Level.CONFIG)) + log.log(Level.CONFIG, "MD5 not available for " + reportLocation, e); + return null; + } + } + + /** + * @author rlemeill + * @param reportLocation http://applicationserver/webApp/standalone.jrxml for + * example + * @param localPath Where to put the http downloaded file + * @return abstract File which represent the downloaded file + */ + private File getRemoteFile(String reportLocation, String localPath) { + try { + URL reportURL = new URL(reportLocation); + try (InputStream in = reportURL.openStream();) { + + File downloadedFile = new File(localPath); + + if (downloadedFile.exists()) { + downloadedFile.delete(); + } + + try (FileOutputStream fout = new FileOutputStream(downloadedFile);) { + byte buf[] = new byte[1024]; + int s = 0; + while ((s = in.read(buf, 0, 1024)) > 0) + fout.write(buf, 0, s); + + fout.flush(); + } + return downloadedFile; + } + } catch (Exception e) { + throw new AdempiereException(e.getMessage(), e); + } + } + + /** + * + * @param path + * @return true if path is http or https + */ + public static boolean isWebResourcePath(String path) { + return path.startsWith("http://") || path.startsWith("https://"); + } +} diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/AEnv.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/AEnv.java index 5c069cfd39..e10f3d6089 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/AEnv.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/AEnv.java @@ -19,12 +19,10 @@ package org.adempiere.webui.apps; import java.io.File; import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; import java.lang.ref.WeakReference; import java.net.URI; import java.net.URISyntaxException; -import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.List; @@ -74,12 +72,7 @@ import org.zkoss.zk.ui.Desktop; import org.zkoss.zk.ui.Execution; import org.zkoss.zk.ui.Executions; -import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; -import com.itextpdf.text.pdf.PdfContentByte; -import com.itextpdf.text.pdf.PdfImportedPage; -import com.itextpdf.text.pdf.PdfReader; -import com.itextpdf.text.pdf.PdfWriter; /** * ZK Application Environment and utilities @@ -615,44 +608,7 @@ public final class AEnv */ public static void mergePdf(List pdfList, File outFile) throws IOException, DocumentException, FileNotFoundException { - Document document = null; - PdfWriter copy = null; - - List pdfReaders = new ArrayList(); - - try - { - for (File f : pdfList) - { - PdfReader reader = new PdfReader(f.getAbsolutePath()); - - pdfReaders.add(reader); - - if (document == null) - { - document = new Document(reader.getPageSizeWithRotation(1)); - copy = PdfWriter.getInstance(document, new FileOutputStream(outFile)); - document.open(); - } - int pages = reader.getNumberOfPages(); - PdfContentByte cb = copy.getDirectContent(); - for (int i = 1; i <= pages; i++) { - document.newPage(); - copy.newPage(); - PdfImportedPage page = copy.getImportedPage(reader, i); - cb.addTemplate(page, 0, 0); - copy.releaseTemplate(page); - } - } - document.close(); - } - finally - { - for(PdfReader reader:pdfReaders) - { - reader.close(); - } - } + Util.mergePdf(pdfList, outFile); } /** diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/ZkJRViewer.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/ZkJRViewer.java index 3aed3bc9f9..e29286156e 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/ZkJRViewer.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/ZkJRViewer.java @@ -15,6 +15,7 @@ import javax.activation.FileDataSource; import org.adempiere.base.upload.IUploadService; import org.adempiere.exceptions.AdempiereException; +import org.adempiere.report.jasper.ReportStarter; import org.adempiere.webui.ClientInfo; import org.adempiere.webui.Extensions; import org.adempiere.webui.LayoutUtils; @@ -174,7 +175,7 @@ public class ZkJRViewer extends Window implements EventListener, ITabOnCl private void init() { final boolean isCanExport=MRole.getDefault().isCanExport(); - defaultType = jasperPrint == null ? null : jasperPrint.getProperty("IDEMPIERE_REPORT_TYPE"); + defaultType = jasperPrint == null ? null : jasperPrint.getProperty(ReportStarter.IDEMPIERE_REPORT_TYPE); if (Util.isEmpty(defaultType)) { defaultType = MSysConfig.getValue(MSysConfig.ZK_REPORT_JASPER_OUTPUT_TYPE, "PDF", Env.getAD_Client_ID(Env.getCtx()), Env.getAD_Org_ID(Env.getCtx()));//It gets default Jasper output type