/* * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU General Public License, version 2 as published by the Free Software * Foundation. * * You should have received a copy of the GNU General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/gpl-2.0.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * 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. * * * Copyright 2006 - 2016 Pentaho Corporation. All rights reserved. */ package org.pentaho.reporting.platform.plugin.async; import org.apache.commons.collections.CollectionUtils; import org.pentaho.reporting.engine.classic.core.ClassicEngineBoot; import org.pentaho.reporting.engine.classic.core.event.ReportProgressEvent; import org.pentaho.reporting.engine.classic.core.event.ReportProgressListener; import org.pentaho.reporting.engine.classic.core.layout.output.AbstractReportProcessor; import org.pentaho.reporting.engine.classic.core.layout.output.ReportProcessorThreadHolder; import org.pentaho.reporting.libraries.base.config.ExtendedConfiguration; import org.pentaho.reporting.libraries.base.util.ArgumentNullException; import java.util.Collections; import java.util.List; import java.util.UUID; /** * Simple synchronized bean with async execution status. This class is part of the PentahoAsyncReportExecution * implementation and should not be used outside of that. */ public class AsyncReportStatusListener implements IAsyncReportListener { private static final String COMPUTING_LAYOUT = "AsyncComputingLayoutTitle"; private static final String PRECOMPUTING_VALUES = "AsyncPrecomputingValuesTitle"; private static final String PAGINATING = "AsyncPaginatingTitle"; private static final String GENERATING_CONTENT = "AsyncGeneratingContentTitle"; private final String path; private final UUID uuid; private final String mimeType; private final List<? extends ReportProgressListener> callbackListeners; private String errorMessage = null; private AsyncExecutionStatus status = AsyncExecutionStatus.QUEUED; private int progress = 0; private int page = 0; private int row = 0; private int totalPages = 0; private int totalRows = 0; private String activity; private boolean firstPageMode = false; private int requestedPage = 0; private int generatedPage = 0; private boolean isQueryLimitReached; private boolean manuallyInterrupted; public AsyncReportStatusListener( final String path, final UUID uuid, final String mimeType, final List<? extends ReportProgressListener> callbackListeners ) { ArgumentNullException.validate( "callbackListeners", callbackListeners ); this.path = path; this.uuid = uuid; this.mimeType = mimeType; this.callbackListeners = Collections.unmodifiableList( callbackListeners ); final ExtendedConfiguration config = ClassicEngineBoot.getInstance().getExtendedConfig(); firstPageMode = config.getBoolProperty( "org.pentaho.reporting.platform.plugin.output.FirstPageMode" ); } @Override public synchronized void setStatus( final AsyncExecutionStatus status ) { if ( this.status == null || !this.status.isFinal() ) { this.status = status; } } @Override public synchronized void setErrorMessage( final String errorMessage ) { this.errorMessage = errorMessage; } public boolean isFirstPageMode() { return firstPageMode; } public synchronized void setRequestedPage( final int requestedPage ) { this.requestedPage = requestedPage; } @Override public synchronized int getRequestedPage() { return requestedPage; } /** * Updates generation status with latest generated page * and restores requested page in order to avoid continuous cache writing * @param generatedPage */ @Override public synchronized void updateGenerationStatus( final int generatedPage ) { this.generatedPage = generatedPage; this.requestedPage = 0; } @Override public synchronized boolean isScheduled() { return AsyncExecutionStatus.SCHEDULED.equals( this.status ); } @Override public synchronized void reportProcessingStarted( final ReportProgressEvent event ) { setStatus( AsyncExecutionStatus.WORKING ); if ( CollectionUtils.isNotEmpty( callbackListeners ) ) { for ( final ReportProgressListener listener : callbackListeners ) { listener.reportProcessingStarted( event ); } } } @Override public synchronized void reportProcessingUpdate( final ReportProgressEvent event ) { if ( manuallyInterrupted ) { final AbstractReportProcessor processor = ReportProcessorThreadHolder.getProcessor(); if ( processor != null ) { processor.cancel(); } } updateState( event ); if ( CollectionUtils.isNotEmpty( callbackListeners ) ) { for ( final ReportProgressListener listener : callbackListeners ) { listener.reportProcessingUpdate( event ); } } } @Override public synchronized void reportProcessingFinished( final ReportProgressEvent event ) { //report is finished but still may be unavailable for client if ( CollectionUtils.isNotEmpty( callbackListeners ) ) { for ( final ReportProgressListener listener : callbackListeners ) { listener.reportProcessingFinished( event ); } } } @Override public String toString() { return "AsyncReportStatusListener{" + "path='" + path + '\'' + ", uuid=" + uuid + ", status=" + status + ", progress=" + progress + ", page=" + page + ", totalPages=" + totalPages + ", generatedPage=" + generatedPage + ", activity='" + activity + '\'' + ", row=" + row + ", firstPageMode=" + firstPageMode + ", mimeType='" + mimeType + '\'' + ", errorMessage='" + errorMessage + '\'' + '}'; } private void updateState( final ReportProgressEvent event ) { this.activity = getActivityCode( event.getActivity() ); this.progress = (int) ReportProgressEvent.computePercentageComplete( event, true ); this.page = event.getPage(); this.row = event.getRow(); this.totalRows = event.getMaximumRow(); this.totalPages = event.getTotalPages(); } private static String getActivityCode( final int activity ) { String result = ""; switch ( activity ) { case ReportProgressEvent.COMPUTING_LAYOUT: { result = COMPUTING_LAYOUT; break; } case ReportProgressEvent.PRECOMPUTING_VALUES: { result = PRECOMPUTING_VALUES; break; } case ReportProgressEvent.PAGINATING: { result = PAGINATING; break; } case ReportProgressEvent.GENERATING_CONTENT: { result = GENERATING_CONTENT; break; } } return result; } public synchronized IAsyncReportState getState() { return new AsyncReportState( uuid, path, status, progress, row, totalRows, page, totalPages, generatedPage, activity, mimeType, errorMessage, isQueryLimitReached ); } public boolean isQueryLimitReached() { return isQueryLimitReached; } @Override public void setIsQueryLimitReached( boolean isQueryLimitReached ) { this.isQueryLimitReached = isQueryLimitReached; } @Override public int getTotalRows() { return totalRows; } public synchronized void cancel() { manuallyInterrupted = true; this.setStatus( AsyncExecutionStatus.CANCELED ); } }