Java tutorial
/* * 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 <http://www.gnu.org/licenses/>. */ package net.sf.jasperreports.web.servlets; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import net.sf.jasperreports.engine.JRRuntimeException; import net.sf.jasperreports.engine.JasperPrint; import net.sf.jasperreports.engine.fill.AsynchronousFilllListener; import net.sf.jasperreports.engine.fill.FillHandle; import net.sf.jasperreports.engine.fill.FillListener; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Generated report accessor used for asynchronous report executions that publishes pages * before the entire report has been generated. * * @author Lucian Chirita (lucianc@users.sourceforge.net) */ public class AsyncJasperPrintAccessor implements JasperPrintAccessor, AsynchronousFilllListener, FillListener { private static final Log log = LogFactory.getLog(AsyncJasperPrintAccessor.class); public static final String EXCEPTION_MESSAGE_KEY_LOCK_ATTEMPT_INTERRUPTED = "web.servlets.lock.attempt.interrupted"; public static final String EXCEPTION_MESSAGE_KEY_NO_JASPERPRINT_GENERATED = "web.servlets.no.jasperprint.generated"; public static final String EXCEPTION_MESSAGE_KEY_REPORT_GENERATION_CANCELLED = "web.servlets.report.generation.cancelled"; public static final String EXCEPTION_MESSAGE_KEY_ASYNC_REPORT_GENERATION_ERROR = "web.servlets.async.report.generation.error"; private FillHandle fillHandle; private final Lock lock; private final Condition pageCondition; private final Map<Integer, Long> trackedPages = new HashMap<Integer, Long>(); private volatile boolean done; private boolean cancelled; private Throwable error; private volatile JasperPrint jasperPrint; private int pageCount; /** * Create a report accessor. * * @param fillHandle the asynchronous fill handle used by this accessor */ public AsyncJasperPrintAccessor(FillHandle fillHandle) { this.fillHandle = fillHandle; lock = new ReentrantLock(true); pageCondition = lock.newCondition(); fillHandle.addListener(this); fillHandle.addFillListener(this); } protected void lock() { try { lock.lockInterruptibly(); } catch (InterruptedException e) { throw new JRRuntimeException(EXCEPTION_MESSAGE_KEY_LOCK_ATTEMPT_INTERRUPTED, (Object[]) null, e); } } protected void unlock() { lock.unlock(); } @Override public ReportPageStatus pageStatus(int pageIdx, Long pageTimestamp) { if (!done) { lock(); try { // wait until the page is available while (!done && pageIdx >= pageCount) { if (log.isDebugEnabled()) { log.debug("waiting for page " + pageIdx); } pageCondition.await(); } } catch (InterruptedException e) { throw new JRRuntimeException(e); } finally { unlock(); } } if (pageIdx >= pageCount) { return ReportPageStatus.NO_SUCH_PAGE; } if (done || fillHandle.isPageFinal(pageIdx)) { trackedPages.remove(pageIdx); return ReportPageStatus.PAGE_FINAL; } long timestamp; boolean modified; Long lastUpdate = trackedPages.get(pageIdx); if (lastUpdate == null) { // we don't know when exactly the page was modified, using current time timestamp = System.currentTimeMillis(); modified = true; } else { timestamp = lastUpdate; modified = pageTimestamp == null || pageTimestamp < lastUpdate; } ReportPageStatus status = ReportPageStatus.nonFinal(timestamp, modified); // add the page to the tracked map so that we catch updates trackedPages.put(pageIdx, timestamp); return status; } @Override public JasperPrint getJasperPrint() { return jasperPrint; } public boolean waitForFinalJasperPrint(int milliseconds) { if (!done) { lock(); try { long waitNanos = TimeUnit.MILLISECONDS.toNanos(milliseconds); // wait until the report generation is done or the time expires while (!done && waitNanos > 0) { if (log.isDebugEnabled()) { log.debug("waiting for report end"); } //FIXME use a condition dedicated to report completion waitNanos = pageCondition.awaitNanos(waitNanos); } } catch (InterruptedException e) { log.error("Error while waiting for final JasperPrint", e); return false; } finally { unlock(); } } return done; } @Override public JasperPrint getFinalJasperPrint() { if (!done) { lock(); try { // wait until the report generation is done while (!done) { if (log.isDebugEnabled()) { log.debug("waiting for report end"); } pageCondition.await();//FIXME use a different condition to void frequent interruptions } } catch (InterruptedException e) { throw new JRRuntimeException(e); } finally { unlock(); } } if (error != null) { throw new JRRuntimeException(EXCEPTION_MESSAGE_KEY_ASYNC_REPORT_GENERATION_ERROR, (Object[]) null, error); } if (jasperPrint == null) { throw new JRRuntimeException(EXCEPTION_MESSAGE_KEY_NO_JASPERPRINT_GENERATED, (Object[]) null); } return jasperPrint; } @Override public void reportFinished(JasperPrint jasperPrint) { if (log.isDebugEnabled()) { log.debug("report finished"); } lock(); try { if (this.jasperPrint == null) { this.jasperPrint = jasperPrint; } pageCount = jasperPrint.getPages().size(); done = true; // clear fillHandle to release filler references fillHandle = null; trackedPages.clear(); pageCondition.signalAll(); } finally { unlock(); } } @Override public void reportCancelled() { if (log.isDebugEnabled()) { log.debug("report cancelled"); } lock(); try { cancelled = true; done = true; pageCount = jasperPrint == null ? 0 : jasperPrint.getPages().size(); // store an error as cancelled status error = new JRRuntimeException(EXCEPTION_MESSAGE_KEY_REPORT_GENERATION_CANCELLED, (Object[]) null); // clear fillHandle to release filler references fillHandle = null; // signal to pageStatus pageCondition.signalAll(); } finally { unlock(); } } @Override public void reportFillError(Throwable t) { log.error("Error during report execution", t); lock(); try { error = t; done = true; pageCount = jasperPrint == null ? 0 : jasperPrint.getPages().size(); // clear fillHandle to release filler references fillHandle = null; // signal to pageStatus pageCondition.signalAll(); } finally { unlock(); } } @Override public void pageGenerated(JasperPrint jasperPrint, int pageIndex) { if (log.isDebugEnabled()) { log.debug("page " + pageIndex + " generated"); } lock(); try { if (this.jasperPrint == null) { this.jasperPrint = jasperPrint; } pageCount = pageIndex + 1; pageCondition.signalAll(); } finally { unlock(); } } @Override public void pageUpdated(JasperPrint jasperPrint, int pageIndex) { if (log.isDebugEnabled()) { log.debug("page " + pageIndex + " updated"); } lock(); try { // update the timestamp if the page is tracked if (trackedPages.containsKey(pageIndex)) { long timestamp = System.currentTimeMillis(); trackedPages.put(pageIndex, timestamp); } } finally { unlock(); } } @Override public ReportExecutionStatus getReportStatus() { if (!done) { return ReportExecutionStatus.running(pageCount); } if (cancelled) { return ReportExecutionStatus.canceled(pageCount); } if (error != null) { return ReportExecutionStatus.error(pageCount, error); } return ReportExecutionStatus.finished(jasperPrint.getPages().size()); } }