/* * 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.cache; import junit.framework.Assert; import org.junit.After; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.pentaho.platform.engine.core.system.PentahoSessionHolder; import org.pentaho.platform.engine.core.system.StandaloneSession; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; 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 junit.framework.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; public class FileSystemCacheBackendTest { private static FileSystemCacheBackend fileSystemCacheBackend; @After public void clearLatchesAfterEachTest() { readStartedSignal = null; writeStartedSignal = null; } private static CountDownLatch readStartedSignal; private static CountDownLatch writeStartedSignal; @BeforeClass public static void setUp() { PentahoSessionHolder.setSession( new StandaloneSession() ); fileSystemCacheBackend = new FileSystemCacheBackend(); fileSystemCacheBackend.setCachePath( "/test-cache/" ); } @AfterClass public static void tearDown() { Assert.assertTrue( fileSystemCacheBackend.purge( Collections.singletonList( "" ) ) ); } private static final String directoryKey = "id344324"; private static final String key = "file1.html"; private static final String value = "SerializableObject"; @Test public void testWriteRead() throws Exception { assertTrue( fileSystemCacheBackend.write( Arrays.asList( directoryKey, key ), value, new HashMap<String, Serializable>() ) ); assertEquals( fileSystemCacheBackend.read( Arrays.asList( directoryKey, key ) ), value ); } @Test public void testPurge() throws Exception { assertTrue( fileSystemCacheBackend.write( Arrays.asList( directoryKey, key ), value, new HashMap<String, Serializable>() ) ); assertEquals( fileSystemCacheBackend.read( Arrays.asList( directoryKey, key ) ), value ); assertTrue( fileSystemCacheBackend.purge( Arrays.asList( directoryKey, key ) ) ); assertNull( fileSystemCacheBackend.read( Arrays.asList( directoryKey, key ) ) ); } @Test public void testPurgeDir() throws Exception { assertTrue( fileSystemCacheBackend.write( Arrays.asList( directoryKey, key ), value, new HashMap<String, Serializable>() ) ); assertEquals( fileSystemCacheBackend.read( Arrays.asList( directoryKey, key ) ), value ); assertTrue( fileSystemCacheBackend.purge( Collections.singletonList( directoryKey ) ) ); assertNull( fileSystemCacheBackend.read( Arrays.asList( directoryKey, key ) ) ); } @Test public void testReadWhileWriting() throws Exception { readStartedSignal = new CountDownLatch( 1 ); writeStartedSignal = new CountDownLatch( 1 ); final String val = "ShinySecretValue"; final ExecutorService executor = Executors.newFixedThreadPool( 2 ); final Future<Boolean> write = executor.submit( new Callable<Boolean>() { @Override public Boolean call() throws Exception { return fileSystemCacheBackend .write( Arrays.asList( directoryKey, key ), new LongWrite( val ), new HashMap<String, Serializable>() ); } } ); final Future<LongWrite> read = executor.submit( new Callable<LongWrite>() { @Override public LongWrite call() throws Exception { writeStartedSignal.await(); // unblock the writer ... readStartedSignal.countDown(); return (LongWrite) fileSystemCacheBackend.read( Arrays.asList( directoryKey, key ) ); } } ); assertTrue( write.get() ); assertEquals( read.get().getValue(), val ); } @Test public void testWriteWhileReading() throws Exception { final String val = "ShinySecretValue"; final String val2 = "ShinySecretValue2"; final ExecutorService executor = Executors.newFixedThreadPool( 2 ); readStartedSignal = null; writeStartedSignal = null; // setup writes without any signals ... fileSystemCacheBackend .write( Arrays.asList( directoryKey, key ), new LongRead( val ), new HashMap<String, Serializable>() ); // now we can setup the signals .. readStartedSignal = new CountDownLatch( 1 ); writeStartedSignal = new CountDownLatch( 1 ); final Future<LongRead> read = executor.submit( new Callable<LongRead>() { @Override public LongRead call() throws Exception { return (LongRead) fileSystemCacheBackend.read( Arrays.asList( directoryKey, key ) ); } } ); final Future<Boolean> write = executor.submit( new Callable<Boolean>() { @Override public Boolean call() throws Exception { readStartedSignal.await(); // unblock the reader ... writeStartedSignal.countDown(); return fileSystemCacheBackend .write( Arrays.asList( directoryKey, key ), new LongRead( val2 ), new HashMap<String, Serializable>() ); } } ); assertTrue( write.get() ); assertEquals( read.get().getValue(), val ); } @Test public void testSystemTmp() { final String property = System.getProperty( "java.io.tmpdir" ); final char c = property.charAt( property.length() - 1 ); if ( ( c == '/' ) || ( c == '\\' ) ) { System.setProperty( "java.io.tmpdir", property.substring( 0, property.length() - 1 ) ); //$NON-NLS-1$//$NON-NLS-2$ } final String systemTmp = fileSystemCacheBackend.getSystemTmp(); Assert.assertEquals( property, systemTmp ); } @Test public void testCorners() throws Exception { Assert.assertNull( fileSystemCacheBackend.read( null ) ); Assert.assertTrue( fileSystemCacheBackend.purge( null ) ); Assert.assertTrue( fileSystemCacheBackend.write( null, "someval", new HashMap<String, Serializable>() ) ); Assert.assertEquals( "someval", fileSystemCacheBackend.read( null ) ); Assert.assertTrue( fileSystemCacheBackend.write( null, null, new HashMap<String, Serializable>() ) ); Assert.assertNull( fileSystemCacheBackend.read( null ) ); Assert.assertTrue( fileSystemCacheBackend.write( null, null, null ) ); Assert.assertFalse( fileSystemCacheBackend.write( null, new FailWrite(), new HashMap<String, Serializable>() { { put( "fail", new FailWrite() ); } } ) ); Assert.assertTrue( fileSystemCacheBackend.write( null, new FailRead(), new HashMap<String, Serializable>() { { put( "fail", new FailRead() ); } } ) ); Assert.assertNull( fileSystemCacheBackend.read( null ) ); } private class LongWrite implements Serializable { private String value; public LongWrite( final String value ) { this.value = value; } public String getValue() { return value; } private void writeObject( final ObjectOutputStream oos ) throws IOException { try { if ( writeStartedSignal != null ) { // unlock the reader ... writeStartedSignal.countDown(); } if ( readStartedSignal != null ) { // wait for the reader to start working .. readStartedSignal.await(); } // now that small time frame should be enough, as we guarantee that the writer is // ready to enter the file-system write method as soon as it sees the latch go. Thread.sleep( 100 ); oos.writeObject( value ); } catch ( final InterruptedException e ) { e.printStackTrace(); } } private void readObject( final ObjectInputStream ois ) throws ClassNotFoundException, IOException { final Object o = ois.readObject(); this.value = (String) o; } } private class LongRead implements Serializable { private String value; public LongRead( final String value ) { this.value = value; } public String getValue() { return value; } private void writeObject( final ObjectOutputStream oos ) throws IOException { oos.writeObject( value ); } private void readObject( final ObjectInputStream ois ) throws ClassNotFoundException, IOException { try { if ( readStartedSignal != null ) { // unlock the writer ... readStartedSignal.countDown(); } if ( writeStartedSignal != null ) { // wait for the writer to start working .. writeStartedSignal.await(); } // now that small time frame should be enough, as we guarantee that the writer is // ready to enter the file-system write method as soon as it sees the latch go. Thread.sleep( 100 ); final Object o = ois.readObject(); this.value = (String) o; } catch ( final InterruptedException e ) { e.printStackTrace(); } } } private class FailWrite implements Serializable { private void writeObject( final ObjectOutputStream oos ) throws IOException { throw new IOException( "C’est la vie" ); } } private class FailRead implements Serializable { String in = "in"; private void readObject( final ObjectOutputStream oos ) throws IOException { throw new IOException( "C’est la vie" ); } private void writeObject( final ObjectOutputStream oos ) throws IOException { oos.writeObject( in ); } } }