net.sf.jasperreports.web.servlets.AsyncJasperPrintAccessor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jasperreports Show documentation
Show all versions of jasperreports Show documentation
Free Java Reporting Library
/*
* 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.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 ([email protected])
*/
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 trackedPages = new HashMap();
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());
}
}