001 package org.hackystat.dailyprojectdata.frontsidecache; 002 003 import java.util.HashMap; 004 import java.util.Map; 005 006 import org.hackystat.dailyprojectdata.server.Server; 007 import org.hackystat.utilities.stacktrace.StackTrace; 008 import org.hackystat.utilities.uricache.UriCache; 009 010 /** 011 * A cache for successfully DPD instances. It is a "front side" cache, in the sense 012 * that it faces the clients, as opposed to the caches associated with the SensorDataClient 013 * instances, which are "back side" in that they cache sensor data instances. While the 014 * "back side" caches avoid calls to the lower-level sensorbase, this front-side cache 015 * avoids the overhead of DPD computation itself. 016 * <p> 017 * The front side cache is organized as follows. Each DPD instance is associated with a 018 * project and a project owner. The FrontSideCache is implemented as a collection of 019 * UriCaches, one for each project owner. When a client adds data to the FrontSideCache, it 020 * must supply the project owner (which is used to figure out which UriCache to use), 021 * the URI of the DPD request (which is the key), and the string representation of the 022 * DPD (which is the value). 023 * <p> 024 * The FrontSideCache currently has hard-coded maxLife of 1000 hours and each UriCache has 025 * a capacity of 1M instances. We could set these via ServerProperties values if necessary. 026 * <p> 027 * There is one important component missing from the FrontSideCache, and that is access 028 * control. The FrontSideCache does not check to see if the client checking the cache has 029 * the right to retrieve the cached data. To perform access control, you should use 030 * the SensorDataClient.inProject(owner, project) method, which checks to see if the 031 * user associated with the SensorDataClient instance has the right to access information 032 * about the project identified by the passed owner/project pair. To see how this 033 * works, here is some example get code, which checks the cache but only returns the 034 * DPD instance if the calling user is in the project: 035 * <pre> 036 * String cachedDpd = this.server.getFrontSideCache().get(uriUser, uriString); 037 * if (cachedDpd != null && client.inProject(authUser, project)) { 038 * return super.getStringRepresentation(cachedDpd); 039 * } 040 * </pre> 041 * 042 * @author Philip Johnson 043 * 044 */ 045 public class FrontSideCache { 046 047 /** The .hackystat subdirectory containing these cache instances. */ 048 private String subDir = "dailyprojectdata/frontsidecache"; 049 050 /** The number of hours that a cached DPD instance stays in the cache before being deleted. */ 051 private double maxLife = 1000; 052 053 /** The total capacity of this cache. */ 054 private long capacity = 1000000L; 055 056 /** Maps user names to their associated UriCache instance. */ 057 private Map<String, UriCache> user2cache = new HashMap<String, UriCache>(); 058 059 /** The server that holds this FrontSideCache. */ 060 private Server server = null; 061 062 /** 063 * Creates a new front-side cache, which stores the DPD instances recently created. 064 * There should be only one of these created for a given DPD server. Note that 065 * this assumes that only one DPD service is running on a given file system. 066 * @param server The DPD server associated with this cache. 067 */ 068 public FrontSideCache(Server server) { 069 this.server = server; 070 } 071 072 /** 073 * Adds a (user, dpd) pair to this front-side cache. 074 * The associated UriCache for this user is created if it does not already exist. 075 * Does nothing if frontsidecaching is disabled. 076 * @param user The user who is the owner of the project associated with this DPD. 077 * @param project The name of the project. 078 * @param uri The URL naming this DPD, as a string. 079 * @param dpdRepresentation A string representing the DPD instance. 080 */ 081 public void put(String user, String project, String uri, String dpdRepresentation) { 082 if (isDisabled()) { 083 return; 084 } 085 try { 086 UriCache uriCache = getCache(user); 087 uriCache.putInGroup(uri, project, dpdRepresentation); 088 } 089 catch (Exception e) { 090 this.server.getLogger().warning("Error during DPD front-side cache add: " + 091 StackTrace.toString(e)); 092 } 093 } 094 095 /** 096 * Returns the string representation of the DPD associated with the DPD owner and the 097 * URI, or null if not in the cache. 098 * @param user The user who is the owner of the Project associated with this DPD. 099 * @param uri The URI naming this DPD. 100 * @param project The project associated with this URI. 101 * @return The string representation of the DPD, or null. 102 */ 103 public String get(String user, String project, String uri) { 104 if (isDisabled()) { 105 return null; 106 } 107 UriCache uriCache = getCache(user); 108 return (String)uriCache.getFromGroup(uri, project); 109 } 110 111 /** 112 * Clears the cache associated with user. Instantiates one if not available so that 113 * any persistent cache that has not yet been read into memory is cleared. 114 * @param user The user whose cache is to be cleared. 115 */ 116 public void clear(String user) { 117 if (isDisabled()) { 118 return; 119 } 120 try { 121 UriCache uriCache = getCache(user); 122 uriCache.clear(); 123 } 124 catch (Exception e) { 125 this.server.getLogger().warning("Error during DPD front-side cache clear: " + 126 StackTrace.toString(e)); 127 } 128 } 129 130 /** 131 * Clears all of the cached DPD instances associated with this project and user. 132 * @param user The user. 133 * @param project The project. 134 */ 135 public void clear(String user, String project) { 136 if (isDisabled()) { 137 return; 138 } 139 try { 140 UriCache uriCache = getCache(user); 141 uriCache.clearGroup(project); 142 } 143 catch (Exception e) { 144 this.server.getLogger().warning("Error during DPD front-side cache clear: " + 145 StackTrace.toString(e)); 146 } 147 } 148 149 150 /** 151 * Returns true if frontsidecaching is disabled. 152 * @return True if disabled. 153 */ 154 private boolean isDisabled() { 155 return !this.server.getServerProperties().isFrontSideCacheEnabled(); 156 } 157 158 /** 159 * Gets the UriCache associated with this project owner from the in-memory map. 160 * Instantiates it if necessary. 161 * @param user The user email (project owner) associated with this UriCache. 162 * @return A UriCache instance for this user. 163 */ 164 private UriCache getCache(String user) { 165 UriCache uriCache = user2cache.get(user); 166 if (uriCache == null) { 167 uriCache = new UriCache(user, subDir, maxLife, capacity); 168 user2cache.put(user, uriCache); 169 } 170 return uriCache; 171 } 172 173 }