/*!
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.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 Lesser General Public License for more details.
*
* Copyright (c) 2002-2016 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.reporting.platform.plugin.output;
import junit.framework.Assert;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.pentaho.platform.api.engine.IPentahoSession;
import org.pentaho.platform.engine.core.system.PentahoSessionHolder;
import org.pentaho.platform.engine.core.system.StandaloneSession;
import org.pentaho.platform.engine.core.system.boot.PlatformInitializationException;
import org.pentaho.reporting.engine.classic.core.ClassicEngineBoot;
import org.pentaho.reporting.engine.classic.core.MasterReport;
import org.pentaho.reporting.engine.classic.core.event.ReportProgressEvent;
import org.pentaho.reporting.libraries.base.config.ModifiableConfiguration;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
import org.pentaho.reporting.platform.plugin.MicroPlatformFactory;
import org.pentaho.reporting.platform.plugin.SimpleReportingComponent;
import org.pentaho.reporting.platform.plugin.async.AsyncExecutionStatus;
import org.pentaho.reporting.platform.plugin.async.IAsyncReportListener;
import org.pentaho.reporting.platform.plugin.async.ReportListenerThreadHolder;
import org.pentaho.reporting.platform.plugin.async.TestListener;
import org.pentaho.reporting.platform.plugin.cache.FileSystemCacheBackend;
import org.pentaho.reporting.platform.plugin.cache.IPluginCacheManager;
import org.pentaho.reporting.platform.plugin.cache.IReportContentCache;
import org.pentaho.reporting.platform.plugin.cache.PluginCacheManagerImpl;
import org.pentaho.reporting.platform.plugin.cache.PluginSessionCache;
import org.pentaho.test.platform.engine.core.MicroPlatform;
import java.io.File;
import java.io.FileOutputStream;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;
public class CachingPageableHTMLOutputIT {
private IAsyncReportListener listener;
SimpleReportingComponent rc;
private static MicroPlatform microPlatform;
private File tmp;
private static FileSystemCacheBackend fileSystemCacheBackend;
private static IPluginCacheManager iPluginCacheManager;
@BeforeClass
public static void setUpClass() throws PlatformInitializationException {
fileSystemCacheBackend = new FileSystemCacheBackend();
fileSystemCacheBackend.setCachePath( "/test-cache/" );
microPlatform = MicroPlatformFactory.create();
microPlatform.define( ReportOutputHandlerFactory.class, FastExportReportOutputHandlerFactory.class );
iPluginCacheManager =
spy( new PluginCacheManagerImpl( new PluginSessionCache( fileSystemCacheBackend ) ) );
microPlatform.define( "IPluginCacheManager", iPluginCacheManager );
microPlatform.start();
}
@AfterClass
public static void tearDownClass() {
Assert.assertTrue( fileSystemCacheBackend.purge( Collections.singletonList( "" ) ) );
microPlatform.stop();
microPlatform = null;
}
@Before public void setUp() {
ClassicEngineBoot.getInstance().start();
listener = mock( IAsyncReportListener.class );
ReportListenerThreadHolder.setListener( listener );
// create an instance of the component
rc = new SimpleReportingComponent();
tmp = new File( "target/test/resource/solution/system/tmp" );
tmp.mkdirs();
final IPentahoSession session = new StandaloneSession();
PentahoSessionHolder.setSession( session );
fileSystemCacheBackend.purge( Collections.singletonList( "" ) );
}
@After public void tearDown() {
ReportListenerThreadHolder.clear();
listener = null;
}
@Test
public void testGenerate() throws Exception {
ReportListenerThreadHolder.clear();
CachingPageableHTMLOutput cachingPageableHTMLOutput = new CachingPageableHTMLOutput();
cachingPageableHTMLOutput.generate( new MasterReport(), 1, new ByteArrayOutputStream(), 1 );
verify( listener, times( 0 ) ).reportProcessingStarted( any( ReportProgressEvent.class ) );
verify( listener, times( 0 ) ).reportProcessingFinished( any( ReportProgressEvent.class ) );
verify( listener, times( 0 ) ).reportProcessingUpdate( any( ReportProgressEvent.class ) );
}
@Test
public void testIsQueryLimitReachedFlagStoredInCacheMetadataTrue() throws Exception {
ReportListenerThreadHolder.clear();
final ModifiableConfiguration edConf = ClassicEngineBoot.getInstance().getEditableConfig();
edConf.setConfigProperty( "org.pentaho.reporting.platform.plugin.output.CachePageableHtmlContent", "true" );
edConf.setConfigProperty( "org.pentaho.reporting.platform.plugin.output.FirstPageMode", "true" );
try {
final TestListener listener = new TestListener( "1", UUID.randomUUID(), "" );
ReportListenerThreadHolder.setListener( listener );
final ResourceManager mgr = new ResourceManager();
final File src = new File( "target/test/resource/solution/test/reporting/report.prpt" );
final MasterReport report = (MasterReport) mgr.createDirectly( src, MasterReport.class ).getResource();
report.setContentCacheKey( "some_key" );
// set row limit for report
report.setQueryLimit( 100 );
execute( report );
assertTrue( listener.isQueryLimitReached() );
final IReportContentCache cache = iPluginCacheManager.getCache();
Map<String, Serializable> metaData = cache.getMetaData( report.getContentCacheKey() );
assertEquals( metaData.get( "IsQueryLimitReached" ), true );
ReportListenerThreadHolder.clear();
assertNull( ReportListenerThreadHolder.getListener() );
} finally {
edConf.setConfigProperty( "org.pentaho.reporting.platform.plugin.output.CachePageableHtmlContent", null );
edConf.setConfigProperty( "org.pentaho.reporting.platform.plugin.output.FirstPageMode", null );
}
}
@Test
public void testIsQueryLimitReachedFlagStoredInCacheMetadataFalse() throws Exception {
ReportListenerThreadHolder.clear();
fileSystemCacheBackend.purge( Collections.singletonList( "" ) );
final ModifiableConfiguration edConf = ClassicEngineBoot.getInstance().getEditableConfig();
edConf.setConfigProperty( "org.pentaho.reporting.platform.plugin.output.CachePageableHtmlContent", "true" );
edConf.setConfigProperty( "org.pentaho.reporting.platform.plugin.output.FirstPageMode", "true" );
try {
final TestListener listener = new TestListener( "1", UUID.randomUUID(), "" );
ReportListenerThreadHolder.setListener( listener );
final ResourceManager mgr = new ResourceManager();
final File src = new File( "target/test/resource/solution/test/reporting/report.prpt" );
final MasterReport report = (MasterReport) mgr.createDirectly( src, MasterReport.class ).getResource();
report.setContentCacheKey( "somekey" );
// set unlimited rows for report
report.setQueryLimit( -1 );
execute( report );
assertFalse( listener.isQueryLimitReached() );
final IReportContentCache cache = iPluginCacheManager.getCache();
Map<String, Serializable> metaData = cache.getMetaData( report.getContentCacheKey() );
assertNull( metaData.get( "IsQueryLimitReached" ) );
ReportListenerThreadHolder.clear();
assertNull( ReportListenerThreadHolder.getListener() );
} finally {
edConf.setConfigProperty( "org.pentaho.reporting.platform.plugin.output.CachePageableHtmlContent", null );
edConf.setConfigProperty( "org.pentaho.reporting.platform.plugin.output.FirstPageMode", null );
}
}
@Test
public void testIsQueryLimitReachedForFirstPageModeDisabled() throws Exception {
ReportListenerThreadHolder.clear();
final ModifiableConfiguration edConf = ClassicEngineBoot.getInstance().getEditableConfig();
edConf.setConfigProperty( "org.pentaho.reporting.platform.plugin.output.CachePageableHtmlContent", "true" );
edConf.setConfigProperty( "org.pentaho.reporting.platform.plugin.output.FirstPageMode", "false" );
try {
final TestListener listener = new TestListener( "1", UUID.randomUUID(), "" );
ReportListenerThreadHolder.setListener( listener );
final ResourceManager mgr = new ResourceManager();
final File src = new File( "target/test/resource/solution/test/reporting/report.prpt" );
final MasterReport report = (MasterReport) mgr.createDirectly( src, MasterReport.class ).getResource();
report.setContentCacheKey( "some_key" );
// set row limit for report
report.setQueryLimit( 20 );
execute( report );
assertTrue( listener.isQueryLimitReached() );
final IReportContentCache cache = iPluginCacheManager.getCache();
Map<String, Serializable> metaData = cache.getMetaData( report.getContentCacheKey() );
assertEquals( metaData.get( "IsQueryLimitReached" ), true );
ReportListenerThreadHolder.clear();
assertNull( ReportListenerThreadHolder.getListener() );
} finally {
edConf.setConfigProperty( "org.pentaho.reporting.platform.plugin.output.CachePageableHtmlContent", null );
edConf.setConfigProperty( "org.pentaho.reporting.platform.plugin.output.FirstPageMode", null );
}
}
private void execute( final MasterReport r ) throws Exception {
// create an instance of the component
final SimpleReportingComponent rc = new SimpleReportingComponent();
// create/set the InputStream
rc.setReport( r );
rc.setOutputType( "text/html" ); //$NON-NLS-1$
// turn on pagination, by way of input (typical mode for xaction)
final HashMap<String, Object> inputs = new HashMap<String, Object>();
inputs.put( "paginate", "true" ); //$NON-NLS-1$ //$NON-NLS-2$
inputs.put( "accepted-page", "0" ); //$NON-NLS-1$ //$NON-NLS-2$
inputs.put( "content-handler-pattern", "test" );
rc.setInputs( inputs );
final FileOutputStream outputStream =
new FileOutputStream( new File( tmp, System.currentTimeMillis() + ".html" ) ); //$NON-NLS-1$ //$NON-NLS-2$
rc.setOutputStream( outputStream );
// execute the component
assertTrue( rc.execute() );
}
@Test
public void testScheduledReportContainsAllPages() throws Exception {
ReportListenerThreadHolder.clear();
final CountDownLatch latch1 = new CountDownLatch( 1 );
final CountDownLatch latch2 = new CountDownLatch( 1 );
final ExecutorService executorService = Executors.newFixedThreadPool( 2 );
final SimpleReportingComponent rc = new SimpleReportingComponent();
final ResourceManager mgr = new ResourceManager();
final File src = new File( "target/test/resource/solution/test/reporting/BigReport.prpt" );
final MasterReport masterReport = (MasterReport) mgr.createDirectly( src, MasterReport.class ).getResource();
final String key = "test";
masterReport.setContentCacheKey( key );
masterReport.setQueryLimit( 500 );
rc.setReport( masterReport );
rc.setOutputType( "text/html" ); //$NON-NLS-1$
// turn on pagination, by way of input (typical mode for xaction)
final HashMap<String, Object> inputs = new HashMap<String, Object>();
inputs.put( "paginate", "true" ); //$NON-NLS-1$ //$NON-NLS-2$
inputs.put( "accepted-page", "0" ); //$NON-NLS-1$ //$NON-NLS-2$
rc.setInputs( inputs );
final TestListener future1Listener = new TestListener( "1", UUID.randomUUID(), "" );
future1Listener.setStatus( AsyncExecutionStatus.SCHEDULED );
final TestListener future2Listener = new TestListener( "1", UUID.randomUUID(), "" );
final Future<byte[]> future1 =
executorService.submit( new CachingPageableHTMLOutputIT.TestTask( latch1, rc, future1Listener ) );
final Future<byte[]> future2 =
executorService.submit( new CachingPageableHTMLOutputIT.TestTask( latch2, rc, future2Listener ) );
latch2.countDown();
while ( !future2.isDone() ) {
if ( future2Listener.isOnFirstPage() ) {
latch1.countDown();
break;
}
Thread.sleep( 10 );
}
final String content2 = new String( future2.get(), "UTF-8" );
assertFalse( content2.contains( "Scheduled paginated HTML report" ) );
final String content1 = new String( future1.get(), "UTF-8" );
assertTrue( content1.contains( "Scheduled paginated HTML report" ) );
assertEquals( future2Listener.getState().getPage(), future1Listener.getState().getTotalPages() );
assertEquals( future1Listener.getState().getPage(), future1Listener.getState().getTotalPages() );
}
private class TestTask implements Callable<byte[]> {
private final CountDownLatch latch;
private final SimpleReportingComponent rc;
private final TestListener listener;
private TestTask( final CountDownLatch latch, final SimpleReportingComponent rc, final TestListener listener ) {
this.latch = latch;
this.rc = rc;
this.listener = listener;
}
@Override public byte[] call() throws Exception {
latch.await();
try ( final java.io.ByteArrayOutputStream outputStream =
new java.io.ByteArrayOutputStream() ) { //$NON-NLS-1$ //$NON-NLS-2$
rc.setOutputStream( outputStream );
ReportListenerThreadHolder.setListener( listener );
rc.execute();
return outputStream.toByteArray();
}
}
}
}