Keesun Baik 2015-02-04
Added AttachmentCache
@0844910716b1127aab2c918ddf9190a23d0d64ea
app/models/Attachment.java
--- app/models/Attachment.java
+++ app/models/Attachment.java
@@ -20,6 +20,7 @@
  */
 package models;
 
+import utils.AttachmentCache;
 import controllers.AttachmentApp;
 import models.enumeration.ResourceType;
 import models.resource.GlobalResource;
@@ -108,9 +109,16 @@
      */
     public static List<Attachment> findByContainer(
             ResourceType containerType, String containerId) {
-        return find.where()
+        List<Attachment> cachedData = AttachmentCache.get(containerType, containerId);
+        if (cachedData != null) {
+            return cachedData;
+        }
+
+        List<Attachment> list = find.where()
                 .eq("containerType", containerType)
                 .eq("containerId", containerId).findList();
+        AttachmentCache.set(containerType.name() + containerId, list);
+        return list;
     }
 
     /**
@@ -120,7 +128,14 @@
      * @return attachments of the container
      */
     public static List<Attachment> findByContainer(Resource container) {
-        return findByContainer(container.getType(), container.getId());
+        List<Attachment> cachedData = AttachmentCache.get(container);
+        if (cachedData != null) {
+            return cachedData;
+        }
+
+        List<Attachment> list = findByContainer(container.getType(), container.getId());
+        AttachmentCache.set(container, list);
+        return list;
     }
 
     /**
@@ -289,7 +304,8 @@
     }
 
     /**
-     * Deletes this file.
+     * Deletes this file and remove cache that contains it.
+     * However, the cache can not be removed if Ebean.delete() is directly used or called by cascading.
      *
      * This method is used when an user delete an attachment or its container.
      */
@@ -311,6 +327,18 @@
                 play.Logger.error("Failed to delete: " + this, e);
             }
         }
+
+        AttachmentCache.remove(this);
+    }
+
+    /**
+     * Update this file and remove cache that contains it.
+     * However, the cache can not be removed if Ebean.update() is directly used or called by cascading.
+     */
+    @Override
+    public void update() {
+        super.update();
+        AttachmentCache.remove(this);
     }
 
 
@@ -434,7 +462,7 @@
                             attachment.delete();
                             deletedFileCount++;
                         }
-                        if( attachmentList.size() != deletedFileCount) {
+                        if (attachmentList.size() != deletedFileCount) {
                             play.Logger.error(
                                     String.format("Failed to delete user temporary files.\nExpected: %d  Actual: %d",
                                             attachmentList.size(), deletedFileCount)
@@ -524,6 +552,8 @@
             this.name = fileName;
         }
 
+        AttachmentCache.remove(this);
+
         // Add the attachment into the Database only if there is no same record.
         Attachment sameAttach = Attachment.findBy(this);
         if (sameAttach == null) {
 
app/utils/AttachmentCache.java (added)
+++ app/utils/AttachmentCache.java
@@ -0,0 +1,104 @@
+package utils;
+
+import models.Attachment;
+import models.enumeration.ResourceType;
+import models.resource.Resource;
+import play.cache.Cache;
+
+import java.util.List;
+
+/**
+ * Utility for caching attachments in a container {@link models.resource.Resource}.
+ *
+ * The key is made by the name of the type of the Resource and Resource's id.
+ * i.e) resource.getType().name() + resource.getId()
+ *
+ * The value is a collection of attachments.
+ *
+ * @author Keeun Baik
+ */
+public class AttachmentCache {
+
+    /**
+     * Play's Cache API allows expiration time in seconds.
+     *
+     * @see {@link play.cache.Cache#set(String, Object, int)}
+     */
+    private static final int ONE_DAY = 60 * 60 * 24;
+
+    /**
+     * Find cached attachments with the key is generated by
+     * combining {@code containerType} and {@code containerId}.
+     *
+     * @param containerType
+     * @param containerId
+     * @return found cached data or null if there is no cached data.
+     */
+    public static List<Attachment> get(ResourceType containerType, String containerId) {
+        String cacheKey = containerType.name() + containerId;
+        Object cachedData = Cache.get(cacheKey);
+        if (cachedData != null) {
+            return (List<Attachment>) cachedData;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Cache attachments with the key
+     *
+     * @param key The key should be generated by combining {@code containerType} and {@code containerId}.
+     * @param list
+     */
+    public static void set(String key, List<Attachment> list) {
+        Cache.set(key, list, ONE_DAY);
+    }
+
+    /**
+     * Cache attachments with the key is generated by {@code container}
+     *
+     * @param container
+     * @param list
+     */
+    public static void set(Resource container, List<Attachment> list) {
+        Cache.set(cacheKey(container), list, ONE_DAY);
+    }
+
+    /**
+     * Find cached attachments with the key is generated by {@code container}
+     *
+     * @param container
+     * @return
+     */
+    public static List<Attachment> get(Resource container) {
+        String cacheKey = cacheKey(container);
+        Object cachedData = Cache.get(cacheKey);
+        if (cachedData != null) {
+            return (List<Attachment>) cachedData;
+        } else {
+            return null;
+        }
+    }
+
+    private static String cacheKey(Resource container) {
+        return container.getType().name() + container.getId();
+    }
+
+    /**
+     * Remove cached attachments with the key is generated by {@code container}
+     *
+     * @param container
+     */
+    public static void remove(Resource container) {
+        Cache.remove(cacheKey(container));
+    }
+
+    /**
+     * Remove cache that contains the {@code attachment}
+     *
+     * @param attachment
+     */
+    public static void remove(Attachment attachment) {
+        Cache.remove(attachment.containerType.name() + attachment.containerId);
+    }
+}
build.sbt
--- build.sbt
+++ build.sbt
@@ -15,6 +15,7 @@
   javaCore,
   javaJdbc,
   javaEbean,
+  cache,
   // Add your project dependencies here,
   "com.h2database" % "h2" % "1.4.184",
   // Core Library
Add a comment
List