diff --git a/jasperreports/src/net/sf/jasperreports/engine/base/ElementsBlock.java b/jasperreports/src/net/sf/jasperreports/engine/base/ElementsBlock.java index c5035b8622..ecf5544239 100644 --- a/jasperreports/src/net/sf/jasperreports/engine/base/ElementsBlock.java +++ b/jasperreports/src/net/sf/jasperreports/engine/base/ElementsBlock.java @@ -103,7 +103,7 @@ public void updatePage(JRVirtualPrintPage page) this.page = page; JRVirtualizationContext newContext = page.getVirtualizationContext(); - context.inheritListeners(newContext); + context.updateParent(newContext); } private void lockContext() diff --git a/jasperreports/src/net/sf/jasperreports/engine/fill/JRTemplatePrintFrame.java b/jasperreports/src/net/sf/jasperreports/engine/fill/JRTemplatePrintFrame.java index 2d50b58e10..b4d9178605 100644 --- a/jasperreports/src/net/sf/jasperreports/engine/fill/JRTemplatePrintFrame.java +++ b/jasperreports/src/net/sf/jasperreports/engine/fill/JRTemplatePrintFrame.java @@ -25,6 +25,7 @@ import java.awt.Color; import java.io.IOException; +import java.io.ObjectInputStream; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -155,4 +156,15 @@ public void readVirtualized(VirtualizationInput in) throws IOException } } } + + private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException + { + in.defaultReadObject(); + if (elements instanceof VirtualizableElementList) + { + VirtualizableElementList virtualizableList = ((VirtualizableElementList) elements); + JRVirtualizationContext virtualizationContext = virtualizableList.getVirtualizationContext(); + virtualizationContext.cacheVirtualizableList(PrintElementId.forElement(this), virtualizableList); + } + } } diff --git a/jasperreports/src/net/sf/jasperreports/engine/fill/JRVirtualizationContext.java b/jasperreports/src/net/sf/jasperreports/engine/fill/JRVirtualizationContext.java index 60ac690065..8e79f6507a 100644 --- a/jasperreports/src/net/sf/jasperreports/engine/fill/JRVirtualizationContext.java +++ b/jasperreports/src/net/sf/jasperreports/engine/fill/JRVirtualizationContext.java @@ -31,6 +31,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.locks.ReentrantLock; @@ -79,6 +80,8 @@ public class JRVirtualizationContext implements Serializable, VirtualizationList ReferenceMap.ReferenceStrength.WEAK, ReferenceMap.ReferenceStrength.WEAK ); + private UUID id; + private boolean root; private transient JRVirtualizationContext parentContext; private transient JRVirtualizer virtualizer; private transient JasperReportsContext jasperReportsContext; @@ -86,8 +89,10 @@ public class JRVirtualizationContext implements Serializable, VirtualizationList private Map cachedRenderers; private Map cachedTemplates; - private Set frameContexts; - private Map virtualizableLists; + private Map subContexts; + + private transient Set frameContexts; + private transient Map virtualizableLists; private volatile boolean readOnly; private volatile boolean disposed; @@ -105,24 +110,31 @@ public class JRVirtualizationContext implements Serializable, VirtualizationList */ public JRVirtualizationContext(JasperReportsContext jasperReportsContext) { + this.id = UUID.randomUUID(); + this.root = true; this.jasperReportsContext = jasperReportsContext; cachedRenderers = new ConcurrentHashMap(16, 0.75f, 1); cachedTemplates = new ConcurrentHashMap(16, 0.75f, 1); virtualizableLists = new ConcurrentHashMap<>(16, 0.75f, 1); + subContexts = new ConcurrentHashMap<>(16, 0.75f, 1); + subContexts.put(this.id, this); + pageElementSize = JRPropertiesUtil.getInstance(jasperReportsContext).getIntegerProperty(JRVirtualPrintPage.PROPERTY_VIRTUAL_PAGE_ELEMENT_SIZE, 0); initLock(); if (log.isDebugEnabled()) { - log.debug("created " + this); + log.debug("created " + id); } } protected JRVirtualizationContext(JRVirtualizationContext parentContext) { + this.id = UUID.randomUUID(); + this.root = false; this.parentContext = parentContext; this.virtualizer = parentContext.virtualizer; this.jasperReportsContext = parentContext.jasperReportsContext; @@ -131,6 +143,9 @@ protected JRVirtualizationContext(JRVirtualizationContext parentContext) this.cachedRenderers = parentContext.cachedRenderers; this.cachedTemplates = parentContext.cachedTemplates; this.virtualizableLists = parentContext.virtualizableLists; + + this.subContexts = new ConcurrentHashMap<>(16, 0.75f, 1); + this.subContexts.put(this.id, this); this.pageElementSize = parentContext.pageElementSize; @@ -139,7 +154,7 @@ protected JRVirtualizationContext(JRVirtualizationContext parentContext) if (log.isDebugEnabled()) { - log.debug("created sub context " + this + ", parent " + parentContext); + log.debug("created sub context " + id + ", parent " + parentContext.id); } } @@ -413,9 +428,10 @@ private void readObject(java.io.ObjectInputStream in) throws IOException, ClassN setThreadJasperReportsContext(); GetField fields = in.readFields(); + root = fields.get("root", false); cachedRenderers = (Map) fields.get("cachedRenderers", null); cachedTemplates = (Map) fields.get("cachedTemplates", null); - virtualizableLists = (Map) fields.get("virtualizableLists", null); + subContexts = (Map) fields.get("subContexts", null); readOnly = fields.get("readOnly", false); // use configured default if serialized by old version pageElementSize = fields.get("pageElementSize", JRPropertiesUtil.getInstance(jasperReportsContext).getIntegerProperty( @@ -424,6 +440,12 @@ private void readObject(java.io.ObjectInputStream in) throws IOException, ClassN setThreadVirtualizer(); initLock(); + + if (root) + { + virtualizableLists = new ConcurrentHashMap<>(16, 0.75f, 1); + subContexts.values().stream().forEach(context -> {context.virtualizableLists = virtualizableLists;}); + } } private void setThreadVirtualizer() @@ -523,6 +545,18 @@ else if (obj instanceof Renderable) replace = new JRVirtualPrintPage.JRIdHolderRenderer(renderer); } } + else if (obj instanceof JRVirtualizationContext) + { + JRVirtualizationContext context = (JRVirtualizationContext) obj; + if (subContexts.containsKey(context.id)) + { + replace = new VirtualizationContextIdHolder(context.id); + } + else if (log.isDebugEnabled()) + { + log.debug("Context " + context.id + " not found under " + id); + } + } return replace; } @@ -555,6 +589,16 @@ else if (obj instanceof JRVirtualPrintPage.JRIdHolderRenderer) } resolve = cachedRenderer; } + else if (obj instanceof VirtualizationContextIdHolder) + { + UUID id = ((VirtualizationContextIdHolder) obj).getId(); + JRVirtualizationContext context = subContexts.get(id); + if (context == null) + { + throw new JRRuntimeException("Context with ID " + id + " not found"); + } + resolve = context; + } return resolve; } @@ -645,6 +689,9 @@ public JRVirtualizationContext getFramesContext() if (frameContexts.isEmpty()) { frameContext = new JRVirtualizationContext(this); + frameContext.subContexts = subContexts; + subContexts.put(frameContext.id, frameContext); + if (listeners != null) { for (VirtualizationListener listener : listeners) @@ -655,7 +702,7 @@ public JRVirtualizationContext getFramesContext() if (log.isDebugEnabled()) { - log.debug(this + " created frames context " + frameContext); + log.debug(id + " created frames context " + frameContext.id); } frameContexts.add(frameContext); @@ -682,11 +729,13 @@ public VirtualizableElementList getVirtualizableList(PrintElementId id) return virtualizableLists.get(id); } - public void inheritListeners(JRVirtualizationContext sourceContext) + public void updateParent(JRVirtualizationContext destinationContext) { - if (sourceContext.listeners != null) + destinationContext.subContexts.put(id, this); + + if (destinationContext.listeners != null) { - for (VirtualizationListener listener : sourceContext.listeners) + for (VirtualizationListener listener : destinationContext.listeners) { if (listeners == null || !listeners.contains(listener))//TODO keep a set? { diff --git a/jasperreports/src/net/sf/jasperreports/engine/fill/VirtualizationContextIdHolder.java b/jasperreports/src/net/sf/jasperreports/engine/fill/VirtualizationContextIdHolder.java new file mode 100644 index 0000000000..721107e83b --- /dev/null +++ b/jasperreports/src/net/sf/jasperreports/engine/fill/VirtualizationContextIdHolder.java @@ -0,0 +1,56 @@ +/* + * JasperReports - Free Java Reporting Library. + * Copyright (C) 2001 - 2019 TIBCO Software Inc. All rights reserved. + * http://www.jaspersoft.com + * + * Unless you have purchased a commercial license agreement from Jaspersoft, + * the following license terms apply: + * + * This program is part of JasperReports. + * + * JasperReports is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JasperReports 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with JasperReports. If not, see . + */ +package net.sf.jasperreports.engine.fill; + +import java.io.Serializable; +import java.util.UUID; + +import net.sf.jasperreports.engine.JRConstants; + +/** + * + * @author Lucian Chirita (lucianc@users.sourceforge.net) + */ +public class VirtualizationContextIdHolder implements Serializable { + + private static final long serialVersionUID = JRConstants.SERIAL_VERSION_UID; + + private UUID id; + + public VirtualizationContextIdHolder() { + } + + public VirtualizationContextIdHolder(UUID id) { + this.id = id; + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + +} diff --git a/jasperreports/tests/net/sf/jasperreports/NoVirtualizerContainer.java b/jasperreports/tests/net/sf/jasperreports/NoVirtualizerContainer.java new file mode 100644 index 0000000000..3a8ae7c508 --- /dev/null +++ b/jasperreports/tests/net/sf/jasperreports/NoVirtualizerContainer.java @@ -0,0 +1,52 @@ +/* + * JasperReports - Free Java Reporting Library. + * Copyright (C) 2001 - 2019 TIBCO Software Inc. All rights reserved. + * http://www.jaspersoft.com + * + * Unless you have purchased a commercial license agreement from Jaspersoft, + * the following license terms apply: + * + * This program is part of JasperReports. + * + * JasperReports is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JasperReports 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with JasperReports. If not, see . + */ +package net.sf.jasperreports; + +import net.sf.jasperreports.engine.JRVirtualizer; + +/** + * @author Lucian Chirita (lucianc@users.sourceforge.net) + */ +public class NoVirtualizerContainer implements VirtualizerContainer +{ + + @Override + public JRVirtualizer getVirtualizer() + { + return null; + } + + @Override + public void setReadOnly() + { + //NOOP + } + + @Override + public void dispose() + { + //NOOP + } + +} diff --git a/jasperreports/tests/net/sf/jasperreports/OwnVirtualizerContainer.java b/jasperreports/tests/net/sf/jasperreports/OwnVirtualizerContainer.java new file mode 100644 index 0000000000..67347c7348 --- /dev/null +++ b/jasperreports/tests/net/sf/jasperreports/OwnVirtualizerContainer.java @@ -0,0 +1,60 @@ +/* + * JasperReports - Free Java Reporting Library. + * Copyright (C) 2001 - 2019 TIBCO Software Inc. All rights reserved. + * http://www.jaspersoft.com + * + * Unless you have purchased a commercial license agreement from Jaspersoft, + * the following license terms apply: + * + * This program is part of JasperReports. + * + * JasperReports is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JasperReports 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with JasperReports. If not, see . + */ +package net.sf.jasperreports; + +import net.sf.jasperreports.engine.JRVirtualizer; +import net.sf.jasperreports.engine.fill.JRAbstractLRUVirtualizer; + +/** + * @author Lucian Chirita (lucianc@users.sourceforge.net) + */ +public class OwnVirtualizerContainer implements VirtualizerContainer +{ + + private final JRAbstractLRUVirtualizer virtualizer; + + public OwnVirtualizerContainer(JRAbstractLRUVirtualizer virtualizer) + { + this.virtualizer = virtualizer; + } + + @Override + public JRVirtualizer getVirtualizer() + { + return virtualizer; + } + + @Override + public void setReadOnly() + { + virtualizer.setReadOnly(true); + } + + @Override + public void dispose() + { + virtualizer.cleanup(); + } + +} diff --git a/jasperreports/tests/net/sf/jasperreports/PrintSerializer.java b/jasperreports/tests/net/sf/jasperreports/PrintSerializer.java new file mode 100644 index 0000000000..bcc603fad6 --- /dev/null +++ b/jasperreports/tests/net/sf/jasperreports/PrintSerializer.java @@ -0,0 +1,97 @@ +/* + * JasperReports - Free Java Reporting Library. + * Copyright (C) 2001 - 2019 TIBCO Software Inc. All rights reserved. + * http://www.jaspersoft.com + * + * Unless you have purchased a commercial license agreement from Jaspersoft, + * the following license terms apply: + * + * This program is part of JasperReports. + * + * JasperReports is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JasperReports 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with JasperReports. If not, see . + */ +package net.sf.jasperreports; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.function.BiConsumer; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import net.sf.jasperreports.engine.JRException; +import net.sf.jasperreports.engine.JRVirtualizer; +import net.sf.jasperreports.engine.JasperPrint; +import net.sf.jasperreports.engine.util.JRLoader; +import net.sf.jasperreports.engine.util.JRSaver; + +/** + * @author Lucian Chirita (lucianc@users.sourceforge.net) + */ +public class PrintSerializer implements BiConsumer +{ + + private static final Log log = LogFactory.getLog(PrintSerializer.class); + + private static final PrintSerializer INSTANCE_NO_VIRTUALIZER = new PrintSerializer(new NoVirtualizerContainer()); + + public static PrintSerializer instance() + { + return INSTANCE_NO_VIRTUALIZER; + } + + private VirtualizerContainer virtualizerContainer; + + public PrintSerializer(VirtualizerContainer virtualizerContainer) + { + this.virtualizerContainer = virtualizerContainer; + } + + @Override + public void accept(Report report, JasperPrint print) + { + try + { + if (log.isDebugEnabled()) + { + log.debug("Serializing report " + report.getJRXML()); + } + + ByteArrayOutputStream printOut = new ByteArrayOutputStream(); + JRSaver.saveObject(print, printOut); + + try + { + JRVirtualizer virtualizer = virtualizerContainer.getVirtualizer(); + if (log.isDebugEnabled()) + { + log.debug("Loading serialized report " + report.getJRXML() + " with virtualizer " + virtualizer); + } + + JasperPrint savedPrint = JRLoader.loadJasperPrint(new ByteArrayInputStream(printOut.toByteArray()), virtualizer); + virtualizerContainer.setReadOnly(); + report.checkDigest(savedPrint); + } + finally + { + virtualizerContainer.dispose(); + } + } + catch (JRException e) + { + throw new RuntimeException(e); + } + } + +} diff --git a/jasperreports/tests/net/sf/jasperreports/Report.java b/jasperreports/tests/net/sf/jasperreports/Report.java index 6c8c05f427..732da6ec52 100644 --- a/jasperreports/tests/net/sf/jasperreports/Report.java +++ b/jasperreports/tests/net/sf/jasperreports/Report.java @@ -37,6 +37,7 @@ import java.util.Locale; import java.util.Map; import java.util.TimeZone; +import java.util.function.BiConsumer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -71,6 +72,7 @@ public class Report private String jrxml; private String jrpxml; + private BiConsumer printConsumer = Report::checkDigest; public Report(String basename) { @@ -108,6 +110,16 @@ public void init() } } + public String getJRXML() + { + return jrxml; + } + + public void addPrintConsumer(BiConsumer printConsumer) + { + this.printConsumer = this.printConsumer.andThen(printConsumer); + } + public JasperReport compileReport() throws JRException, IOException { InputStream jrxmlInput = JRLoader.getResourceInputStream(jrxml); @@ -146,7 +158,7 @@ public void runReport(Map params) JasperPrint print = fillManager.fill(report, reportParams); reportComplete(reportParams, print); } - catch (JRException | NoSuchAlgorithmException | IOException e) + catch (JRException e) { throw new RuntimeException(e); } @@ -164,7 +176,6 @@ protected Map reportParams(Map params) } protected void reportComplete(Map params, JasperPrint print) - throws NoSuchAlgorithmException, FileNotFoundException, JRException, IOException { JRVirtualizer virtualizer = (JRVirtualizer) params.get(JRParameter.REPORT_VIRTUALIZER); if (virtualizer instanceof JRAbstractLRUVirtualizer) @@ -174,9 +185,7 @@ protected void reportComplete(Map params, JasperPrint print) assert !print.getPages().isEmpty(); - String digestString = xmlDigest(print); - log.debug("Report " + jrxml + " got " + digestString); - assert digestString.equals(referenceJRPXMLDigest); + printConsumer.accept(this, print); if (virtualizer != null) { @@ -184,6 +193,20 @@ protected void reportComplete(Map params, JasperPrint print) } } + public void checkDigest(JasperPrint print) + { + try + { + String digestString = xmlDigest(print); + log.debug("Report " + jrxml + " got " + digestString); + assert digestString.equals(referenceJRPXMLDigest); + } + catch (NoSuchAlgorithmException | JRException | IOException e) + { + throw new RuntimeException(e); + } + } + protected String xmlDigest(JasperPrint print) throws NoSuchAlgorithmException, FileNotFoundException, JRException, IOException { diff --git a/jasperreports/tests/net/sf/jasperreports/VirtualizerContainer.java b/jasperreports/tests/net/sf/jasperreports/VirtualizerContainer.java new file mode 100644 index 0000000000..65a475f89e --- /dev/null +++ b/jasperreports/tests/net/sf/jasperreports/VirtualizerContainer.java @@ -0,0 +1,40 @@ +/* + * JasperReports - Free Java Reporting Library. + * Copyright (C) 2001 - 2019 TIBCO Software Inc. All rights reserved. + * http://www.jaspersoft.com + * + * Unless you have purchased a commercial license agreement from Jaspersoft, + * the following license terms apply: + * + * This program is part of JasperReports. + * + * JasperReports is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * JasperReports 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with JasperReports. If not, see . + */ +package net.sf.jasperreports; + +import net.sf.jasperreports.engine.JRVirtualizer; + +/** + * @author Lucian Chirita (lucianc@users.sourceforge.net) + */ +public interface VirtualizerContainer +{ + + JRVirtualizer getVirtualizer(); + + void setReadOnly(); + + void dispose(); + +} diff --git a/jasperreports/tests/net/sf/jasperreports/async/AsyncReport.java b/jasperreports/tests/net/sf/jasperreports/async/AsyncReport.java index b546db18ff..9a992c668d 100644 --- a/jasperreports/tests/net/sf/jasperreports/async/AsyncReport.java +++ b/jasperreports/tests/net/sf/jasperreports/async/AsyncReport.java @@ -23,8 +23,6 @@ */ package net.sf.jasperreports.async; -import java.io.IOException; -import java.security.NoSuchAlgorithmException; import java.util.Map; import net.sf.jasperreports.Report; @@ -63,7 +61,7 @@ public void runReport(Map params, FillListener fillListener) JasperPrint print = accessor.getFinalJasperPrint(); reportComplete(reportParams, print); } - catch (JRException | NoSuchAlgorithmException | IOException e) + catch (JRException e) { throw new RuntimeException(e); } diff --git a/jasperreports/tests/net/sf/jasperreports/virtualization/ReportTest.java b/jasperreports/tests/net/sf/jasperreports/virtualization/ReportTest.java index 38ce026bb6..5669b0afd8 100644 --- a/jasperreports/tests/net/sf/jasperreports/virtualization/ReportTest.java +++ b/jasperreports/tests/net/sf/jasperreports/virtualization/ReportTest.java @@ -30,6 +30,8 @@ import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import net.sf.jasperreports.OwnVirtualizerContainer; +import net.sf.jasperreports.PrintSerializer; import net.sf.jasperreports.Report; import net.sf.jasperreports.engine.JRException; import net.sf.jasperreports.engine.JRParameter; @@ -48,6 +50,8 @@ public void initReport() throws JRException, IOException { report = new Report("net/sf/jasperreports/virtualization/repo/FirstJasper.jrxml", "net/sf/jasperreports/virtualization/FirstJasper.reference.jrpxml"); + report.addPrintConsumer(PrintSerializer.instance()); + report.addPrintConsumer(new PrintSerializer(new OwnVirtualizerContainer(new JRGzipVirtualizer(5)))); report.init(); } diff --git a/jasperreports/tests/net/sf/jasperreports/virtualization/VirtualizedFrames.reference.jrpxml b/jasperreports/tests/net/sf/jasperreports/virtualization/VirtualizedFrames.reference.jrpxml new file mode 100644 index 0000000000..3b244e37bc --- /dev/null +++ b/jasperreports/tests/net/sf/jasperreports/virtualization/VirtualizedFrames.reference.jrpxml @@ -0,0 +1,1756 @@ + + + + + + +