package pl.net.bluesoft.rnd.pt.ext.report.step; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.converters.MarshallingContext; import com.thoughtworks.xstream.converters.collections.CollectionConverter; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import com.thoughtworks.xstream.mapper.Mapper; import net.sf.jasperreports.engine.*; import net.sf.jasperreports.engine.data.JRXmlDataSource; import net.sf.jasperreports.engine.export.*; import net.sf.jasperreports.engine.query.JRXPathQueryExecuterFactory; import org.apache.chemistry.opencmis.client.api.Folder; import org.apache.commons.codec.binary.Base64; import org.hibernate.collection.PersistentSet; import pl.net.bluesoft.rnd.processtool.ProcessToolContext; import pl.net.bluesoft.rnd.processtool.model.BpmStep; import pl.net.bluesoft.rnd.processtool.model.ProcessInstance; import pl.net.bluesoft.rnd.processtool.model.ProcessInstanceAttribute; import pl.net.bluesoft.rnd.processtool.model.ProcessInstanceSimpleAttribute; import pl.net.bluesoft.rnd.processtool.steps.ProcessToolProcessStep; import pl.net.bluesoft.rnd.processtool.ui.widgets.annotations.AliasName; import pl.net.bluesoft.rnd.processtool.ui.widgets.annotations.AutoWiredProperty; import pl.net.bluesoft.rnd.pt.ext.report.model.ReportDAO; import pl.net.bluesoft.rnd.pt.ext.report.model.ReportTemplate; import pl.net.bluesoft.rnd.pt.ext.report.util.dict.DictionaryHelperImpl; import pl.net.bluesoft.rnd.pt.utils.cmis.CmisAtomSessionFacade; import pl.net.bluesoft.util.lang.StringUtil; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; import java.text.SimpleDateFormat; import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; import static pl.net.bluesoft.util.lang.StringUtil.hasText; @AliasName(name = "GenerateDocumentStep") public class GenerateDocument implements ProcessToolProcessStep { private static final String DATETIME_PATTERN = "dd-MM-yyyy HH:mm"; private static final String PROCESS_INSTANCE_KEY = "processInstance"; private static final String PROCESS_INSTANCE_ID_KEY = "processInstanceId"; private static final String PROCESS_CONTEXT_KEY = "processContext"; private static final String DICTIONARY_HELPER = "dictionaryHelper"; private static final Locale DEFAULT_LOCALE = Locale.ENGLISH; private static final String DEFAULT_ENCODING = "Cp1250"; private static class MyPersistentSetConverter extends CollectionConverter { public MyPersistentSetConverter(Mapper mapper) { super(mapper); } @Override public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { PersistentSet ps = (PersistentSet) source; super.marshal(new HashSet(ps), writer, context); } @Override public boolean canConvert(Class type) { return type.isAssignableFrom(PersistentSet.class); } } public class ReportException extends Exception { private static final long serialVersionUID = 4702732796115095701L; public ReportException(String message) { super("Report '" + reportName + "': " + message); } public ReportException(String message, Throwable t) { super("Report '" + reportName + "': " + message, t); } } private Logger logger = Logger.getLogger(GenerateDocument.class.getName()); @AutoWiredProperty private String reportName; @AutoWiredProperty private String defaultLocaleName; @AutoWiredProperty private String localeAttributeKey = "java.util.Locale"; @AutoWiredProperty private String format = "PDF"; @AutoWiredProperty private Locale locale = DEFAULT_LOCALE; @AutoWiredProperty private String encoding = DEFAULT_ENCODING; @AutoWiredProperty private String outputFileName = "report.pdf"; @AutoWiredProperty private String mimeType = "application/pdf"; @AutoWiredProperty private String repositoryAtomUrl = "http://dreihund:8080/alfresco/service/cmis"; @AutoWiredProperty private String repositoryId = "default"; @AutoWiredProperty private String repositoryUser = "awf"; @AutoWiredProperty private String repositoryPassword = "awf"; @AutoWiredProperty private String rootFolderPath = "/processtool/docs"; @AutoWiredProperty private String subFolder = "test1"; @AutoWiredProperty private String newFolderPrefix = "pt_"; private CmisAtomSessionFacade sessionFacade; private Folder mainFolder; @AutoWiredProperty private String popup = ""; private static final String POPUP_ONCE = "ONCE"; private static final String POPUP_ALWAYS = "ALWAYS"; @Override public String invoke(BpmStep step, Map<String, String> params) throws Exception { try { ProcessInstance processInstance = step.getProcessInstance(); // GET PARAMETERS FOR REPORT reportName = (String) params.get("reportName"); subFolder = (String) params.get("subFolder"); outputFileName = (String) params.get("outputFileName"); rootFolderPath = (String) params.get("rootFolderPath"); repositoryAtomUrl = (String) params.get("repositoryAtomUrl"); repositoryId = (String) params.get("repositoryId"); repositoryUser = (String) params.get("repositoryUser"); repositoryPassword = (String) params.get("repositoryPassword"); if (params.get("localeAttributeKey") != null) { localeAttributeKey = (String) params.get("localeAttributeKey"); } defaultLocaleName = (String) params.get("defaultLocaleName"); initLocale(processInstance); // BUILD REPORT logger.warning("GenerateDocument start, building report"); byte[] report = buildReport(processInstance); logger.warning("report built"); saveReport(processInstance, report); logger.warning("report saved"); return "OK"; } catch (Exception e) { logger.log(Level.SEVERE, e.getMessage(), e); return "ERROR"; } } public static Locale getLocaleFromString(String localeString) { if (localeString == null) { return null; } localeString = localeString.trim(); if (localeString.toLowerCase().equals("default")) { return Locale.getDefault(); } int languageIndex = localeString.indexOf('_'); String language = null; if (languageIndex == -1) { return new Locale(localeString, ""); } else { language = localeString.substring(0, languageIndex); } int countryIndex = localeString.indexOf('_', languageIndex + 1); String country = null; if (countryIndex == -1) { country = localeString.substring(languageIndex + 1); return new Locale(language, country); } else { country = localeString.substring(languageIndex + 1, countryIndex); String variant = localeString.substring(countryIndex + 1); return new Locale(language, country, variant); } } private void initLocale(ProcessInstance processInstance) { if (hasText(localeAttributeKey)) { ProcessInstanceAttribute attr = processInstance.findAttributeByKey(localeAttributeKey); if (attr != null && attr instanceof ProcessInstanceSimpleAttribute) { locale = getLocaleFromString(((ProcessInstanceSimpleAttribute)attr).getValue()); } } } private void saveReport(ProcessInstance processInstance, byte[] report) { Thread t = Thread.currentThread(); ClassLoader previousLoader = t.getContextClassLoader(); try { ClassLoader newClassLoader = ProcessToolContext.Util.getThreadProcessToolContext() .getRegistry().getModelAwareClassLoader(getClass().getClassLoader()); t.setContextClassLoader(newClassLoader); sessionFacade = new CmisAtomSessionFacade(repositoryUser, repositoryPassword, repositoryAtomUrl, repositoryId); mainFolder = sessionFacade.createFolderIfNecessary(newFolderPrefix + processInstance.getInternalId(), rootFolderPath); if (StringUtil.hasText(subFolder)) mainFolder = sessionFacade.createFolderIfNecessary(subFolder, mainFolder.getPath()); outputFileName = outputFileName.replace("#{processId}", String.valueOf(processInstance.getInternalId())); outputFileName = outputFileName.replace("#{date}", new SimpleDateFormat("dd.MM.yyyy").format(new Date())); outputFileName = outputFileName.replace("#{datetime}", new SimpleDateFormat("dd.MM.yyyy HH.mm").format(new Date())); if(POPUP_ONCE.equalsIgnoreCase(popup)) outputFileName += "__POPUP_" + POPUP_ONCE + "__"; if(POPUP_ALWAYS.equalsIgnoreCase(popup)) outputFileName += "__POPUP_" + POPUP_ALWAYS + "__"; sessionFacade.uploadDocument(outputFileName, mainFolder, report, mimeType, null); logger.info("MAIN_FOLDER_PATH: " + mainFolder.getPath()); } finally { t.setContextClassLoader(previousLoader); } } protected byte[] buildReport(ProcessInstance processInstance) throws ReportException { // LOAD TEMPLATE ReportTemplate template = new ReportDAO().loadByName(reportName); if (template == null) throw new ReportException("Report template does not exist!"); // COMPILE REPORT JasperReport jasperReport = null; ProcessToolContext ctx = ProcessToolContext.Util.getThreadProcessToolContext(); try { ByteArrayInputStream contentInputStream = getContentInputStream(template.getContent()); Thread t = Thread.currentThread(); ClassLoader previousLoader = t.getContextClassLoader(); try { ClassLoader newClassLoader = ctx.getRegistry().getModelAwareClassLoader(getClass().getClassLoader()); t.setContextClassLoader(newClassLoader); jasperReport = JasperCompileManager.compileReport(contentInputStream); } finally { t.setContextClassLoader(previousLoader); } } catch (Exception e) { throw new ReportException("Report compilation failed!", e); } // PREPARE INPUT Map<String, Object> parameters = new HashMap<String, Object>(); parameters.put(JRXPathQueryExecuterFactory.XML_DATE_PATTERN, DATETIME_PATTERN); parameters.put(JRParameter.REPORT_LOCALE, locale); parameters.put(PROCESS_CONTEXT_KEY, ctx); // FILL REPORT JasperPrint jasperPrint = null; try { Thread t = Thread.currentThread(); ClassLoader previousLoader = t.getContextClassLoader(); try { ClassLoader newClassLoader = ctx .getRegistry().getModelAwareClassLoader(getClass().getClassLoader()); t.setContextClassLoader(newClassLoader); parameters.put(DICTIONARY_HELPER, new DictionaryHelperImpl(processInstance)); if (jasperReport.getQuery() != null && jasperReport.getQuery().getLanguage().equals("xPath")) { XStream xs = new XStream(); xs.registerConverter(new MyPersistentSetConverter(xs.getMapper()), XStream.PRIORITY_VERY_HIGH); xs.omitField(ProcessInstance.class, "definition"); xs.omitField(ProcessInstance.class, "processLogs"); String s = xs.toXML(processInstance); jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, new JRXmlDataSource( new ByteArrayInputStream(s.getBytes()) )); } else if (jasperReport.getQuery() != null && hasText(jasperReport.getQuery().getText())) { parameters.put(PROCESS_INSTANCE_ID_KEY, String.valueOf(processInstance.getId())); jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, ctx.getHibernateSession().connection()); } else { parameters.put(PROCESS_INSTANCE_KEY, processInstance); jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, new JREmptyDataSource()); } } finally { t.setContextClassLoader(previousLoader); } } catch (Exception e) { throw new ReportException("Report filling failed!", e); } // EXPORT REPORT byte[] report = null; try { Thread t = Thread.currentThread(); ClassLoader previousLoader = t.getContextClassLoader(); try { ClassLoader newClassLoader = ctx .getRegistry().getModelAwareClassLoader(getClass().getClassLoader()); t.setContextClassLoader(newClassLoader); report = exportReport(jasperPrint, format, encoding); } finally { t.setContextClassLoader(previousLoader); } } catch (Exception e) { throw new ReportException("Report export failed!", e); } return report; } private ByteArrayInputStream getContentInputStream(String content) { try { return new ByteArrayInputStream(Base64.decodeBase64(content.getBytes("UTF-8"))); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } private final String FIELD_DELIMITER = ";"; private final String RECORD_DELIMITER = "\n\r"; public byte[] exportReport(JasperPrint jasperPrint, String outputFormat, String characterEncoding) throws ReportException { try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); JRExporter exporter; if ("PDF".equalsIgnoreCase(outputFormat)) { exporter = new JRPdfExporter(); exporter.setParameter(JRPdfExporterParameter.CHARACTER_ENCODING, characterEncoding); // exporter.setParameter(JRPdfExporterParameter.CLASS_LOADER, getClass().getClassLoader()); } else if ("HTML".equalsIgnoreCase(outputFormat)) { exporter = new JRHtmlExporter(); exporter.setParameter(JRHtmlExporterParameter.IS_USING_IMAGES_TO_ALIGN, Boolean.FALSE); exporter.setParameter(JRHtmlExporterParameter.IGNORE_PAGE_MARGINS, Boolean.TRUE); } else if ("XLS".equalsIgnoreCase(outputFormat)) { exporter = new JRXlsExporter(); exporter.setParameter(JRXlsExporterParameter.CHARACTER_ENCODING, characterEncoding); exporter.setParameter(JRXlsExporterParameter.IGNORE_PAGE_MARGINS, Boolean.TRUE); exporter.setParameter(JRXlsExporterParameter.IS_DETECT_CELL_TYPE, Boolean.TRUE); exporter.setParameter(JRXlsExporterParameter.IS_WHITE_PAGE_BACKGROUND, false); } else if ("CSV".equalsIgnoreCase(outputFormat)) { exporter = new JRCsvExporter(); exporter.setParameter(JRCsvExporterParameter.CHARACTER_ENCODING, characterEncoding); exporter.setParameter(JRCsvExporterParameter.RECORD_DELIMITER, RECORD_DELIMITER); exporter.setParameter(JRCsvExporterParameter.FIELD_DELIMITER, FIELD_DELIMITER); } else { String message = "Invalid report type. Permitted types are: HTML, PDF, XLS, CSV"; throw new ReportException(message); } exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint); exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, bos); exporter.exportReport(); return bos.toByteArray(); } catch (JRException e) { throw new ReportException(e.getMessage(), e); } } public void setReportName(String reportName) { this.reportName = reportName; } public void setFormat(String format) { this.format = format; } public void setLocale(Locale locale) { this.locale = locale; } public void setEncoding(String encoding) { this.encoding = encoding; } public void setRepositoryAtomUrl(String repositoryAtomUrl) { this.repositoryAtomUrl = repositoryAtomUrl; } public void setRepositoryId(String repositoryId) { this.repositoryId = repositoryId; } public void setRepositoryUser(String repositoryUser) { this.repositoryUser = repositoryUser; } public void setRepositoryPassword(String repositoryPassword) { this.repositoryPassword = repositoryPassword; } public void setRootFolderPath(String rootFolderPath) { this.rootFolderPath = rootFolderPath; } public void setSubFolder(String subFolder) { this.subFolder = subFolder; } public void setNewFolderPrefix(String newFolderPrefix) { this.newFolderPrefix = newFolderPrefix; } public void setOutputFileName(String outputFileName) { this.outputFileName = outputFileName; } public void setMimeType(String mimeType) { this.mimeType = mimeType; } public void setPopup(String popup) { this.popup = popup; } }