From c3ca707bffa2085bb86d35e3c3097cf80365a133 Mon Sep 17 00:00:00 2001 From: Matt Ward Date: Wed, 17 Aug 2011 15:12:35 +0000 Subject: [PATCH] ALF-9613: (fake) slow content store and CachingContentStore tests to use it. Created a stub content store that reads and writes very slowly and end-to-end tests that utilise the slow store to prove there is a speed up on reading and writing through the CachingContentStore. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@29838 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../caching/test/SlowContentStore.java | 229 ++++++++++++++++++ .../caching/test/SlowContentStoreTest.java | 140 +++++++++++ .../cachingstore/test-slow-context.xml | 8 + 3 files changed, 377 insertions(+) create mode 100644 source/java/org/alfresco/repo/content/caching/test/SlowContentStore.java create mode 100644 source/java/org/alfresco/repo/content/caching/test/SlowContentStoreTest.java create mode 100644 source/test-resources/cachingstore/test-slow-context.xml diff --git a/source/java/org/alfresco/repo/content/caching/test/SlowContentStore.java b/source/java/org/alfresco/repo/content/caching/test/SlowContentStore.java new file mode 100644 index 0000000000..b1a4752b05 --- /dev/null +++ b/source/java/org/alfresco/repo/content/caching/test/SlowContentStore.java @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco 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. + * + * Alfresco 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 Alfresco. If not, see . + */ + +package org.alfresco.repo.content.caching.test; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.WritableByteChannel; + +import org.alfresco.repo.content.AbstractContentReader; +import org.alfresco.repo.content.AbstractContentStore; +import org.alfresco.repo.content.AbstractContentWriter; +import org.alfresco.repo.content.filestore.FileContentStore; +import org.alfresco.service.cmr.repository.ContentIOException; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentWriter; + +/** + * Package-private class - only for testing the CachingContentStore. + * + * @author Matt Ward + */ +class SlowContentStore extends AbstractContentStore +{ + + /* + * @see org.alfresco.repo.content.ContentStore#isWriteSupported() + */ + @Override + public boolean isWriteSupported() + { + return true; + } + + /* + * @see org.alfresco.repo.content.ContentStore#getReader(java.lang.String) + */ + @Override + public ContentReader getReader(String contentUrl) + { + return new SlowReader(contentUrl); + } + + @Override + protected ContentWriter getWriterInternal(ContentReader existingContentReader, String newContentUrl) + { + if (newContentUrl == null) + newContentUrl = FileContentStore.createNewFileStoreUrl() + ".slow"; + + return new SlowWriter(newContentUrl, existingContentReader); + } + + + @Override + public boolean exists(String contentUrl) + { + return false; + } + + private class SlowWriter extends AbstractContentWriter + { + protected SlowWriter(String contentUrl, ContentReader existingContentReader) + { + super(contentUrl, existingContentReader); + } + + @Override + public long getSize() + { + return 20; + } + + @Override + protected ContentReader createReader() throws ContentIOException + { + return new SlowReader(getContentUrl()); + } + + @Override + protected WritableByteChannel getDirectWritableChannel() throws ContentIOException + { + return new WritableByteChannel() + { + private boolean closed = false; + private int left = 200; + + @Override + public boolean isOpen() + { + return !closed; + } + + @Override + public void close() throws IOException + { + closed = true; + } + + @Override + public int write(ByteBuffer src) throws IOException + { + try + { + Thread.sleep(50); + } + catch (InterruptedException error) + { + throw new RuntimeException(error); + } + + if (left > 0) + { + src.get(); + left--; + return 1; + } + return 0; + } + }; + } + + } + + + private class SlowReader extends AbstractContentReader + { + protected SlowReader(String contentUrl) + { + super(contentUrl); + } + + @Override + public boolean exists() + { + return true; + } + + @Override + public long getLastModified() + { + return 0L; + } + + @Override + public long getSize() + { + return 20; + } + + @Override + protected ContentReader createReader() throws ContentIOException + { + return new SlowReader(getContentUrl()); + } + + @Override + protected ReadableByteChannel getDirectReadableChannel() throws ContentIOException + { + return new ReadableByteChannel() + { + private final byte[] content = "This is the content for my slow ReadableByteChannel".getBytes(); + private int index = 0; + private boolean closed = false; + + @Override + public boolean isOpen() + { + return !closed; + } + + @Override + public void close() throws IOException + { + closed = true; + } + + @Override + public int read(ByteBuffer dst) throws IOException + { + if (index < content.length) + { + try + { + Thread.sleep(50); + } + catch (InterruptedException error) + { + throw new RuntimeException(error); + } + dst.put(content[index++]); + return 1; + } + else + { + return 0; + } + } + }; + } + + } + + + public static void main(String[] args) + { + SlowContentStore scs = new SlowContentStore(); + + ContentReader reader = scs.getReader("store://something/bin"); + String content = reader.getContentString(); + System.out.println("Content: " + content); + } +} diff --git a/source/java/org/alfresco/repo/content/caching/test/SlowContentStoreTest.java b/source/java/org/alfresco/repo/content/caching/test/SlowContentStoreTest.java new file mode 100644 index 0000000000..bcc3fae181 --- /dev/null +++ b/source/java/org/alfresco/repo/content/caching/test/SlowContentStoreTest.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco 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. + * + * Alfresco 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 Alfresco. If not, see . + */ + +package org.alfresco.repo.content.caching.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.alfresco.repo.content.ContentContext; +import org.alfresco.repo.content.caching.CachingContentStore; +import org.alfresco.util.ApplicationContextHelper; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.Test; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * Tests that exercise the CachingContentStore in conjunction with a backing store + * that runs deliberately slowly. + * + * @author Matt Ward + */ +public class SlowContentStoreTest +{ + private ClassPathXmlApplicationContext ctx; + private CachingContentStore cachingStore; + private static final Log logger = LogFactory.getLog(SlowContentStoreTest.class); + + + public SlowContentStoreTest() + { + String conf = "classpath:cachingstore/test-context.xml"; + String slowconf = "classpath:cachingstore/test-slow-context.xml"; + ctx = (ClassPathXmlApplicationContext) ApplicationContextHelper.getApplicationContext(new String[] { conf, slowconf }); + cachingStore = (CachingContentStore) ctx.getBean("cachingContentStore"); + cachingStore.setCacheOnInbound(false); + } + + + @Test + public void readsAreFasterFromCache() + { + // First read will hit the SLOW backing store + TimedStoreReader storeReader = new TimedStoreReader(); + storeReader.execute(); + assertTrue("First read should take a while", storeReader.timeTakenMillis() > 1000); + logger.info(String.format("First read took %ds", storeReader.timeTakenMillis())); + // The content came from the slow backing store... + assertEquals("This is the content for my slow ReadableByteChannel", storeReader.content); + + // Subsequent reads will hit the cache + for (int i = 0; i < 5; i++) + { + storeReader = new TimedStoreReader(); + storeReader.execute(); + assertTrue("Subsequent reads should be fast", storeReader.timeTakenMillis() < 100); + logger.info(String.format("Cache read took %ds", storeReader.timeTakenMillis())); + // The content came from the slow backing store, but was cached... + assertEquals("This is the content for my slow ReadableByteChannel", storeReader.content); + } + } + + + @Test + public void writeThroughCacheResultsInFastReadFirstTime() + { + cachingStore.setCacheOnInbound(true); + + // This content will be cached on the way in + cachingStore.getWriter(new ContentContext(null, "any-url")). + putContent("Content written from " + getClass().getSimpleName()); + + // First read will hit cache + TimedStoreReader storeReader = new TimedStoreReader(); + storeReader.execute(); + assertTrue("First read should be fast", storeReader.timeTakenMillis() < 100); + logger.info(String.format("First read took %ds", storeReader.timeTakenMillis())); + assertEquals("Content written from " + getClass().getSimpleName(), storeReader.content); + + // Subsequent reads will also hit the cache + for (int i = 0; i < 5; i++) + { + storeReader = new TimedStoreReader(); + storeReader.execute(); + assertTrue("Subsequent reads should be fast", storeReader.timeTakenMillis() < 100); + logger.info(String.format("Cache read took %ds", storeReader.timeTakenMillis())); + // The original cached content, still cached... + assertEquals("Content written from " + getClass().getSimpleName(), storeReader.content); + } + } + + + private class TimedStoreReader extends TimedExecutor + { + String content; + + @Override + protected void doExecute() + { + content = cachingStore.getReader("any-url").getContentString(); + logger.info("Read content: " + content); + } + } + + private abstract class TimedExecutor + { + private long start; + private long finish; + + public void execute() + { + start = System.currentTimeMillis(); + doExecute(); + finish = System.currentTimeMillis(); + } + + public long timeTakenMillis() + { + return finish - start; + } + + protected abstract void doExecute(); + } +} diff --git a/source/test-resources/cachingstore/test-slow-context.xml b/source/test-resources/cachingstore/test-slow-context.xml new file mode 100644 index 0000000000..512936c474 --- /dev/null +++ b/source/test-resources/cachingstore/test-slow-context.xml @@ -0,0 +1,8 @@ + + + + + + + +