From 13fa49303f686609f108e7f9be6af45e88a75a34 Mon Sep 17 00:00:00 2001
From: brinn <brinn>
Date: Mon, 10 Dec 2012 08:47:05 +0000
Subject: [PATCH] [CCS-22/SP-398] Genedata Performance Tuning (improve loading
 speed of images through the API) Cache open HDF5 archive readers until 5 min
 of inactivity.

SVN: 27888
---
 .../common/hdf5/HDF5ContainerReader.java      | 108 ++++++++++++++++--
 1 file changed, 100 insertions(+), 8 deletions(-)

diff --git a/openbis-common/source/java/ch/systemsx/cisd/openbis/common/hdf5/HDF5ContainerReader.java b/openbis-common/source/java/ch/systemsx/cisd/openbis/common/hdf5/HDF5ContainerReader.java
index 1f41475e002..01c3e0e0fb5 100644
--- a/openbis-common/source/java/ch/systemsx/cisd/openbis/common/hdf5/HDF5ContainerReader.java
+++ b/openbis-common/source/java/ch/systemsx/cisd/openbis/common/hdf5/HDF5ContainerReader.java
@@ -18,7 +18,12 @@ package ch.systemsx.cisd.openbis.common.hdf5;
 
 import java.io.File;
 import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
 
 import ch.systemsx.cisd.hdf5.h5ar.ArchiveEntry;
 import ch.systemsx.cisd.hdf5.h5ar.HDF5ArchiverFactory;
@@ -32,41 +37,128 @@ import ch.systemsx.cisd.hdf5.h5ar.ListParameters;
  */
 final class HDF5ContainerReader implements IHDF5ContainerReader
 {
-    private final IHDF5ArchiveReader archiveReader;
+    private static final long CACHE_CLEANER_INTERVAL_MILLIS = 60000L;
+
+    /**
+     * A container for reader which stores last access time and current reference count.
+     */
+    private static class Reader
+    {
+        private final static long RETENTION_TIME_MILLIS = 5 * 60 * 1000L; // 5 minutes
+
+        private final IHDF5ArchiveReader archiveReader;
+
+        private long lastAccessed;
+
+        private int referenceCount;
+
+        Reader(IHDF5ArchiveReader archiveReader)
+        {
+            this.archiveReader = archiveReader;
+            this.lastAccessed = System.currentTimeMillis();
+            this.referenceCount = 1;
+        }
+
+        IHDF5ArchiveReader getArchiveReader()
+        {
+            this.lastAccessed = System.currentTimeMillis();
+            return archiveReader;
+        }
+
+        void incCount()
+        {
+            ++referenceCount;
+        }
+
+        void decCount()
+        {
+            --referenceCount;
+        }
+
+        boolean isExpired()
+        {
+            return (referenceCount == 0 && (System.currentTimeMillis() - lastAccessed) > RETENTION_TIME_MILLIS);
+        }
+    }
+    
+    static
+    {
+        final Timer t = new Timer("HDF5ContainerReader - Cache Cleaner", true);
+        t.schedule(new TimerTask() {
+            @Override
+            public void run()
+            {
+                synchronized (fileToReaderMap)
+                {
+                    final Iterator<Reader> it = fileToReaderMap.values().iterator();
+                    while (it.hasNext())
+                    {
+                        final Reader container = it.next();
+                        if (container.isExpired())
+                        {
+                            it.remove();
+                        }
+                    }
+                }
+            } } , CACHE_CLEANER_INTERVAL_MILLIS, CACHE_CLEANER_INTERVAL_MILLIS);
+    }
+
+    private static final Map<File, Reader> fileToReaderMap =
+            new HashMap<File, HDF5ContainerReader.Reader>();
+    
+    private static Reader openReader(File hdf5Container)
+    {
+        Reader entryOrNull;
+        synchronized (fileToReaderMap)
+        {
+            entryOrNull = fileToReaderMap.get(hdf5Container);
+            if (entryOrNull == null)
+            {
+                entryOrNull = new Reader(HDF5ArchiverFactory.openForReading(hdf5Container));
+                fileToReaderMap.put(hdf5Container, entryOrNull);
+            } else
+            {
+                entryOrNull.incCount();
+            }
+        }
+        return entryOrNull;
+    }
+
+    private final Reader reader;
 
     HDF5ContainerReader(final File hdf5Container)
     {
-        this.archiveReader = HDF5ArchiverFactory.openForReading(hdf5Container);
+        this.reader = openReader(hdf5Container);
     }
 
     @Override
     public void close()
     {
-        archiveReader.close();
+        reader.decCount();
     }
 
     @Override
     public boolean exists(String objectPath)
     {
-        return archiveReader.exists(objectPath);
+        return reader.getArchiveReader().exists(objectPath);
     }
 
     @Override
     public ArchiveEntry tryGetEntry(String path)
     {
-        return archiveReader.tryGetResolvedEntry(path, true);
+        return reader.getArchiveReader().tryGetResolvedEntry(path, true);
     }
 
     @Override
     public List<ArchiveEntry> getGroupMembers(String groupPath)
     {
-        return archiveReader.list(groupPath, ListParameters.build().nonRecursive()
+        return reader.getArchiveReader().list(groupPath, ListParameters.build().nonRecursive()
                 .resolveSymbolicLinks().get());
     }
 
     @Override
     public void readFromHDF5Container(String objectPath, OutputStream ostream)
     {
-        archiveReader.extractFile(objectPath, ostream);
-  }
+        reader.getArchiveReader().extractFile(objectPath, ostream);
+    }
 }
-- 
GitLab