/*********************************************************************** * 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.Arrays; import java.util.Enumeration; import java.util.Optional; import java.util.logging.Level; import org.compiere.util.CLogger; import org.compiere.util.Language; import org.compiere.utils.DigestOfFile; import org.osgi.framework.Bundle; /** * * 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; String bundleName = null; //check is resource name include optional bundle symbolic name (syntax -> bundle:bundleSymbolicName:jasperReportFileName) if (resourceName.indexOf(":") > 0) { bundleName = resourceName.substring(0, resourceName.indexOf(":")); resourceName = resourceName.substring(resourceName.indexOf(":")+1); bundleFileName = resourceName; } if (resourceName.lastIndexOf("/") > 0) { parentPath = "/"+resourceName.substring(0, resourceName.lastIndexOf("/")); bundleFileName = resourceName.substring(resourceName.lastIndexOf("/")+1); } URL url = null; Bundle bundle = null; //if resource name include optional bundle symbolic name, load from specific bundle if (bundleName == null) { bundle = Activator.getBundleContext().getBundle(); } else { String symbolicName = bundleName; Optional optional = Arrays.stream(Activator.getBundleContext().getBundles()).filter(b -> b.getSymbolicName().equals(symbolicName)).findFirst(); if (optional.isPresent()) bundle = optional.get(); } Enumeration entries = bundle != null ? bundle.findEntries(parentPath, bundleFileName, false) : null; 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(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(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 + 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); } }