| 1 | <!----------------------------------------------------------------------- |
|---|
| 2 | ******************************************************************************** |
|---|
| 3 | Copyright 2005-2007 ColdBox Framework by Luis Majano and Ortus Solutions, Corp |
|---|
| 4 | www.coldboxframework.com | www.luismajano.com | www.ortussolutions.com |
|---|
| 5 | ******************************************************************************** |
|---|
| 6 | |
|---|
| 7 | Author : Luis Majano |
|---|
| 8 | Date : January 18, 2007 |
|---|
| 9 | Description : |
|---|
| 10 | This is a cfc that handles caching of event handlers. |
|---|
| 11 | |
|---|
| 12 | Modification History: |
|---|
| 13 | 01/18/2007 - Created |
|---|
| 14 | |
|---|
| 15 | -----------------------------------------------------------------------> |
|---|
| 16 | <cfcomponent name="cacheManager" hint="Manages handler,plugin,custom plugin and object caching. It is thread safe and implements locking for you." output="false"> |
|---|
| 17 | |
|---|
| 18 | <!------------------------------------------- CONSTRUCTOR -------------------------------------------> |
|---|
| 19 | |
|---|
| 20 | <cffunction name="init" access="public" output="false" returntype="cacheManager" hint="Constructor"> |
|---|
| 21 | <cfargument name="controller" type="any" required="true"> |
|---|
| 22 | <cfscript> |
|---|
| 23 | variables.instance = structnew(); |
|---|
| 24 | //Set Controller Injection |
|---|
| 25 | instance.controller = arguments.controller; |
|---|
| 26 | //Cache Configuration |
|---|
| 27 | instance.CacheConfigBean = structnew(); |
|---|
| 28 | //Object Pool |
|---|
| 29 | instance.objectPool = structnew(); |
|---|
| 30 | //Cache Performance |
|---|
| 31 | instance.cachePerformance = structNew(); |
|---|
| 32 | instance.cachePerformance.Hits = 0; |
|---|
| 33 | instance.cachePerformance.Misses = 0; |
|---|
| 34 | //Reaping Controll |
|---|
| 35 | instance.lastReapDatetime = now(); |
|---|
| 36 | //Runtime Java object |
|---|
| 37 | instance.javaRuntime = CreateObject("java", "java.lang.Runtime"); |
|---|
| 38 | //Lock Name |
|---|
| 39 | instance.lockName = getController().getAppHash() & "_OCM_OPERATION"; |
|---|
| 40 | //Init the object Pool on instantiation |
|---|
| 41 | initPool(); |
|---|
| 42 | //return Cache Manager reference; |
|---|
| 43 | return this; |
|---|
| 44 | </cfscript> |
|---|
| 45 | </cffunction> |
|---|
| 46 | |
|---|
| 47 | <cffunction name="configure" access="public" output="false" returntype="void" hint="Configures the cache for operation."> |
|---|
| 48 | <cfargument name="cacheConfigBean" type="coldbox.system.beans.cacheConfigBean" required="true"> |
|---|
| 49 | <cfscript> |
|---|
| 50 | //set the config bean |
|---|
| 51 | setCacheConfigBean(arguments.cacheConfigBean); |
|---|
| 52 | //Reset the statistics. |
|---|
| 53 | resetStatistics(); |
|---|
| 54 | </cfscript> |
|---|
| 55 | </cffunction> |
|---|
| 56 | |
|---|
| 57 | <!------------------------------------------- PUBLIC -------------------------------------------> |
|---|
| 58 | |
|---|
| 59 | <!--- ************************************************************* ---> |
|---|
| 60 | |
|---|
| 61 | <cffunction name="lookup" access="public" output="false" returntype="boolean" hint="Check if an object is in cache, if not found it records a miss."> |
|---|
| 62 | <!--- ************************************************************* ---> |
|---|
| 63 | <cfargument name="objectKey" type="string" required="true" hint="The key of the object to lookup."> |
|---|
| 64 | <!--- ************************************************************* ---> |
|---|
| 65 | <cfset var ObjectFound = false> |
|---|
| 66 | |
|---|
| 67 | <cflock type="readonly" name="#getLockName()#" timeout="30"> |
|---|
| 68 | <cfif getobjectPool().lookup(arguments.objectKey)> |
|---|
| 69 | <cfset ObjectFound = true> |
|---|
| 70 | <cfelse> |
|---|
| 71 | <!--- Log miss ---> |
|---|
| 72 | <cfset miss()> |
|---|
| 73 | </cfif> |
|---|
| 74 | </cflock> |
|---|
| 75 | |
|---|
| 76 | <cfreturn ObjectFound> |
|---|
| 77 | </cffunction> |
|---|
| 78 | |
|---|
| 79 | <!--- ************************************************************* ---> |
|---|
| 80 | |
|---|
| 81 | <cffunction name="get" access="public" output="false" returntype="any" hint="Get an object from cache. If it doesn't exist it returns a blank structure."> |
|---|
| 82 | <!--- ************************************************************* ---> |
|---|
| 83 | <cfargument name="objectKey" type="string" required="true" hint="The key of the object to lookup."> |
|---|
| 84 | <!--- ************************************************************* ---> |
|---|
| 85 | <cfset var ObjectFound = StructNew()> |
|---|
| 86 | |
|---|
| 87 | <!--- Lookup First ---> |
|---|
| 88 | <cfif lookup(arguments.objectKey)> |
|---|
| 89 | <cflock type="exclusive" name="#getLockName()#" timeout="30"> |
|---|
| 90 | <!--- Record a Hit ---> |
|---|
| 91 | <cfset hit()> |
|---|
| 92 | <cfset ObjectFound = getobjectPool().get(arguments.objectKey)> |
|---|
| 93 | </cflock> |
|---|
| 94 | </cfif> |
|---|
| 95 | <cfreturn ObjectFound> |
|---|
| 96 | </cffunction> |
|---|
| 97 | |
|---|
| 98 | <!--- ************************************************************* ---> |
|---|
| 99 | |
|---|
| 100 | <cffunction name="set" access="public" output="false" returntype="void" hint="sets an object in cache."> |
|---|
| 101 | <!--- ************************************************************* ---> |
|---|
| 102 | <cfargument name="objectKey" type="string" required="true"> |
|---|
| 103 | <cfargument name="MyObject" type="any" required="true"> |
|---|
| 104 | <cfargument name="Timeout" type="string" required="false" default="" hint="Timeout in minutes. If timeout = 0 then object never times out. If timeout is blank, then timeout will be inherited from framework."> |
|---|
| 105 | <!--- ************************************************************* ---> |
|---|
| 106 | <!---JVM Threshold Checks ---> |
|---|
| 107 | <cfset var isBelowThreshold = ThresholdChecks()> |
|---|
| 108 | <cfset var ccBean = getCacheConfigBean()> |
|---|
| 109 | |
|---|
| 110 | <!--- Clean Args ---> |
|---|
| 111 | <cfset arguments.objectKey = trim(arguments.objectKey)> |
|---|
| 112 | <cfset arguments.Timeout = trim(arguments.Timeout)> |
|---|
| 113 | |
|---|
| 114 | <!--- Max Objects in Cache Check ---> |
|---|
| 115 | <cfif (ccBean.getCacheMaxObjects() eq 0 or getSize() lt ccBean.getCacheMaxObjects()) and |
|---|
| 116 | (ccBean.getCacheFreeMemoryPercentageThreshold() eq 0 or isBelowThreshold)> |
|---|
| 117 | |
|---|
| 118 | <!--- Test Timeout Argument, if false, then inherit framework's timeout ---> |
|---|
| 119 | <cfif arguments.Timeout eq "" or not isNumeric(arguments.Timeout) or arguments.Timeout lt 0> |
|---|
| 120 | <cfset arguments.Timeout = ccBean.getCacheObjectDefaultTimeout()> |
|---|
| 121 | </cfif> |
|---|
| 122 | |
|---|
| 123 | <!--- Set object in Cache ---> |
|---|
| 124 | <cflock type="exclusive" name="#getLockName()#" timeout="30"> |
|---|
| 125 | <cfset getobjectPool().set(arguments.objectKey,arguments.MyObject,arguments.Timeout)> |
|---|
| 126 | </cflock> |
|---|
| 127 | </cfif> |
|---|
| 128 | </cffunction> |
|---|
| 129 | |
|---|
| 130 | <!--- ************************************************************* ---> |
|---|
| 131 | |
|---|
| 132 | <cffunction name="clearKey" access="public" output="false" returntype="boolean" hint="Clears a key from the cache."> |
|---|
| 133 | <!--- ************************************************************* ---> |
|---|
| 134 | <cfargument name="objectKey" type="string" required="true"> |
|---|
| 135 | <!--- ************************************************************* ---> |
|---|
| 136 | <cfset var Results = false> |
|---|
| 137 | <cfif getobjectPool().lookup(arguments.objectKey) > |
|---|
| 138 | <cflock type="exclusive" name="#getLockName()#" timeout="30"> |
|---|
| 139 | <cfset Results = getobjectPool().clearKey(arguments.objectKey)> |
|---|
| 140 | </cflock> |
|---|
| 141 | </cfif> |
|---|
| 142 | <cfreturn Results> |
|---|
| 143 | </cffunction> |
|---|
| 144 | |
|---|
| 145 | <!--- ************************************************************* ---> |
|---|
| 146 | |
|---|
| 147 | <cffunction name="clear" access="public" output="false" returntype="void" hint="Clears the entire object cache. Call from a non-cached object or you will get 500 NULL errors, VERY VERY BAD!!."> |
|---|
| 148 | <cflock type="exclusive" name="#getLockName()#" timeout="30"> |
|---|
| 149 | <cfset structDelete(variables,"objectPool")> |
|---|
| 150 | <cfset initPool()> |
|---|
| 151 | <cfset resetStatistics()> |
|---|
| 152 | </cflock> |
|---|
| 153 | </cffunction> |
|---|
| 154 | |
|---|
| 155 | <!--- ************************************************************* ---> |
|---|
| 156 | |
|---|
| 157 | <cffunction name="resetStatistics" access="public" output="false" returntype="void" hint="Resets the cache statistics."> |
|---|
| 158 | <cfscript> |
|---|
| 159 | getcachePerformance().Hits = 0; |
|---|
| 160 | getcachePerformance().Misses = 0; |
|---|
| 161 | </cfscript> |
|---|
| 162 | </cffunction> |
|---|
| 163 | |
|---|
| 164 | <!--- ************************************************************* ---> |
|---|
| 165 | |
|---|
| 166 | <cffunction name="getCachePerformanceRatio" access="public" output="false" returntype="numeric" hint="Get the cache's performance ratio"> |
|---|
| 167 | <cfscript> |
|---|
| 168 | var requests = getcachePerformance().hits + getcachePerformance().misses; |
|---|
| 169 | if ( requests eq 0) |
|---|
| 170 | return 0; |
|---|
| 171 | else |
|---|
| 172 | return (getcachePerformance().Hits/requests) * 100; |
|---|
| 173 | </cfscript> |
|---|
| 174 | </cffunction> |
|---|
| 175 | |
|---|
| 176 | <!--- ************************************************************* ---> |
|---|
| 177 | |
|---|
| 178 | <cffunction name="getSize" access="public" output="false" returntype="numeric" hint="Get the cache's size in items"> |
|---|
| 179 | <cfscript> |
|---|
| 180 | return getObjectPool().getSize(); |
|---|
| 181 | </cfscript> |
|---|
| 182 | </cffunction> |
|---|
| 183 | |
|---|
| 184 | <!--- ************************************************************* ---> |
|---|
| 185 | |
|---|
| 186 | <cffunction name="getpool_metadata" access="public" returntype="struct" output="false" hint="Get the pool's metadata structure"> |
|---|
| 187 | <cfreturn getObjectPool().getpool_metadata()> |
|---|
| 188 | </cffunction> |
|---|
| 189 | |
|---|
| 190 | <!--- ************************************************************* ---> |
|---|
| 191 | |
|---|
| 192 | <cffunction name="getItemTypes" access="public" output="false" returntype="struct" hint="Get the item types of the cache."> |
|---|
| 193 | <cfscript> |
|---|
| 194 | var x = 1; |
|---|
| 195 | var itemList = getObjectPool().getObjectsKeyList(); |
|---|
| 196 | var itemTypes = Structnew(); |
|---|
| 197 | |
|---|
| 198 | //Init types |
|---|
| 199 | itemTypes.plugins = 0; |
|---|
| 200 | itemTypes.handlers = 0; |
|---|
| 201 | itemTypes.other = 0; |
|---|
| 202 | itemTypes.ioc_beans = 0; |
|---|
| 203 | |
|---|
| 204 | //Sort the listing. |
|---|
| 205 | itemList = listSort(itemList, "textnocase"); |
|---|
| 206 | |
|---|
| 207 | //Count objects |
|---|
| 208 | for (x=1; x lte listlen(itemList) ; x = x+1){ |
|---|
| 209 | if ( findnocase("plugin", listGetAt(itemList,x)) ) |
|---|
| 210 | itemTypes.plugins = itemTypes.plugins + 1; |
|---|
| 211 | else if ( findnocase("handler", listGetAt(itemList,x)) ) |
|---|
| 212 | itemTypes.handlers = itemTypes.handlers + 1; |
|---|
| 213 | else if ( findnocase("ioc", listGetAt(itemList,x)) ) |
|---|
| 214 | itemTypes.ioc_beans = itemTypes.ioc_beans + 1; |
|---|
| 215 | else |
|---|
| 216 | itemTypes.other = itemTypes.other + 1; |
|---|
| 217 | } |
|---|
| 218 | return itemTypes; |
|---|
| 219 | </cfscript> |
|---|
| 220 | </cffunction> |
|---|
| 221 | |
|---|
| 222 | <!--- ************************************************************* ---> |
|---|
| 223 | |
|---|
| 224 | <cffunction name="reap" access="public" output="false" returntype="void" hint="Reap the cache."> |
|---|
| 225 | <cfscript> |
|---|
| 226 | var key = ""; |
|---|
| 227 | var objStruct = getObjectPool().getpool_metadata(); |
|---|
| 228 | var ccBean = getCacheConfigBean(); |
|---|
| 229 | |
|---|
| 230 | //Check if no data in pool |
|---|
| 231 | if (not structisEmpty(objStruct)){ |
|---|
| 232 | |
|---|
| 233 | //Check reaping frequency |
|---|
| 234 | if ( dateDiff("n", getlastReapDatetime(), now() ) gte ccBean.getCacheReapFrequency() ){ |
|---|
| 235 | |
|---|
| 236 | //Reaping about to start, set new reaping date. |
|---|
| 237 | setlastReapDatetime( now() ); |
|---|
| 238 | |
|---|
| 239 | //Loop Through Metadata |
|---|
| 240 | for (key in objStruct){ |
|---|
| 241 | //Override Timeout Check |
|---|
| 242 | if ( objStruct[key].Timeout gt 0 ){ |
|---|
| 243 | //Check for creation timeouts and clear |
|---|
| 244 | if ( dateDiff("n", objStruct[key].created, now() ) gte objStruct[key].Timeout ){ |
|---|
| 245 | clearKey(key); |
|---|
| 246 | continue; |
|---|
| 247 | } |
|---|
| 248 | //Check for last accessed timeout. If object has not been accessed in the default span |
|---|
| 249 | if ( dateDiff("n", objStruct[key].lastAccesed, now() ) gte ccBean.getCacheObjectDefaultLastAccessTimeout() ){ |
|---|
| 250 | clearKey(key); |
|---|
| 251 | continue; |
|---|
| 252 | } |
|---|
| 253 | }//end timeout gt 0 |
|---|
| 254 | }//end for loop |
|---|
| 255 | }// end reaping frequency check |
|---|
| 256 | }//end if objects in pool |
|---|
| 257 | </cfscript> |
|---|
| 258 | </cffunction> |
|---|
| 259 | |
|---|
| 260 | <!--- ************************************************************* ---> |
|---|
| 261 | |
|---|
| 262 | <cffunction name="expireAll" access="public" returntype="any" hint="Expire All Objects. Use this instead of clear() from within handlers or any cached object, this sets the metadata for the objects to expire in the next request. Note that this is not an inmmediate expiration. Clear should only be used from outside a cached object" output="false" > |
|---|
| 263 | <cfscript> |
|---|
| 264 | var key = ""; |
|---|
| 265 | var objStruct = getObjectPool().getpool_metadata(); |
|---|
| 266 | |
|---|
| 267 | //Check if no data in pool |
|---|
| 268 | if (not structisEmpty(objStruct)){ |
|---|
| 269 | //Loop Through Metadata and set expiration timeouts. |
|---|
| 270 | for (key in objStruct){ |
|---|
| 271 | objStruct[key].Timeout = 1; |
|---|
| 272 | objStruct[key].created = dateadd("n",-5,now()); |
|---|
| 273 | } |
|---|
| 274 | } |
|---|
| 275 | </cfscript> |
|---|
| 276 | </cffunction> |
|---|
| 277 | |
|---|
| 278 | <!------------------------------------------- ACCESSOR/MUTATORS -------------------------------------------> |
|---|
| 279 | |
|---|
| 280 | <cffunction name="getlastReapDatetime" access="public" output="false" returntype="string" hint="Get the lastReapDatetime"> |
|---|
| 281 | <cfscript> |
|---|
| 282 | return instance.lastReapDatetime; |
|---|
| 283 | </cfscript> |
|---|
| 284 | </cffunction> |
|---|
| 285 | <cffunction name="setlastReapDatetime" access="public" returntype="void" output="false" hint="Set the lastReapDatetime"> |
|---|
| 286 | <cfargument name="lastReapDatetime" type="string" required="true"> |
|---|
| 287 | <cfset instance.lastReapDatetime = arguments.lastReapDatetime> |
|---|
| 288 | </cffunction> |
|---|
| 289 | |
|---|
| 290 | <!--- ************************************************************* ---> |
|---|
| 291 | |
|---|
| 292 | <cffunction name="getcachePerformance" access="public" output="false" returntype="struct" hint="Get the cachePerformance structure"> |
|---|
| 293 | <cfscript> |
|---|
| 294 | return instance.cachePerformance; |
|---|
| 295 | </cfscript> |
|---|
| 296 | </cffunction> |
|---|
| 297 | |
|---|
| 298 | <!--- ************************************************************* ---> |
|---|
| 299 | |
|---|
| 300 | <cffunction name="setCacheConfigBean" access="public" returntype="void" output="false"> |
|---|
| 301 | <cfargument name="CacheConfigBean" type="coldbox.system.beans.cacheConfigBean" required="true"> |
|---|
| 302 | <cfset instance.CacheConfigBean = arguments.CacheConfigBean> |
|---|
| 303 | </cffunction> |
|---|
| 304 | <cffunction name="getCacheConfigBean" access="public" returntype="coldbox.system.beans.cacheConfigBean" output="false"> |
|---|
| 305 | <cfreturn instance.CacheConfigBean > |
|---|
| 306 | </cffunction> |
|---|
| 307 | |
|---|
| 308 | <!--- ************************************************************* ---> |
|---|
| 309 | |
|---|
| 310 | <cffunction name="getjavaRuntime" access="public" returntype="any" output="false" hint="Get the java runtime object."> |
|---|
| 311 | <cfreturn instance.javaRuntime> |
|---|
| 312 | </cffunction> |
|---|
| 313 | |
|---|
| 314 | <!--- ************************************************************* ---> |
|---|
| 315 | |
|---|
| 316 | <cffunction name="getcontroller" access="public" output="false" returntype="any" hint="Get controller"> |
|---|
| 317 | <cfreturn instance.controller/> |
|---|
| 318 | </cffunction> |
|---|
| 319 | |
|---|
| 320 | <!--- ************************************************************* ---> |
|---|
| 321 | |
|---|
| 322 | <cffunction name="setcontroller" access="public" output="false" returntype="void" hint="Set controller"> |
|---|
| 323 | <cfargument name="controller" type="any" required="true"/> |
|---|
| 324 | <cfset instance.controller = arguments.controller/> |
|---|
| 325 | </cffunction> |
|---|
| 326 | |
|---|
| 327 | <!--- ************************************************************* ---> |
|---|
| 328 | |
|---|
| 329 | <cffunction name="getlockName" access="public" output="false" returntype="string" hint="Get lockName"> |
|---|
| 330 | <cfreturn instance.lockName/> |
|---|
| 331 | </cffunction> |
|---|
| 332 | |
|---|
| 333 | <!--- ************************************************************* ---> |
|---|
| 334 | |
|---|
| 335 | <cffunction name="setlockName" access="public" output="false" returntype="void" hint="Set lockName"> |
|---|
| 336 | <cfargument name="lockName" type="string" required="true"/> |
|---|
| 337 | <cfset instance.lockName = arguments.lockName/> |
|---|
| 338 | </cffunction> |
|---|
| 339 | |
|---|
| 340 | <!--- ************************************************************* ---> |
|---|
| 341 | |
|---|
| 342 | <cffunction name="getObjectPool" access="public" returntype="any" output="false" hint="Get the internal object pool"> |
|---|
| 343 | <cfreturn instance.objectPool > |
|---|
| 344 | </cffunction> |
|---|
| 345 | |
|---|
| 346 | <!--- ************************************************************* ---> |
|---|
| 347 | |
|---|
| 348 | <!------------------------------------------- PRIVATE -------------------------------------------> |
|---|
| 349 | |
|---|
| 350 | <cffunction name="hit" access="private" output="false" returntype="void" hint="Record a hit"> |
|---|
| 351 | <cfscript> |
|---|
| 352 | getcachePerformance().Hits = getcachePerformance().Hits + 1; |
|---|
| 353 | </cfscript> |
|---|
| 354 | </cffunction> |
|---|
| 355 | |
|---|
| 356 | <!--- ************************************************************* ---> |
|---|
| 357 | |
|---|
| 358 | <cffunction name="miss" access="private" output="false" returntype="void" hint="Record a miss"> |
|---|
| 359 | <cfscript> |
|---|
| 360 | getcachePerformance().misses = getcachePerformance().misses + 1; |
|---|
| 361 | </cfscript> |
|---|
| 362 | </cffunction> |
|---|
| 363 | |
|---|
| 364 | <!--- ************************************************************* ---> |
|---|
| 365 | |
|---|
| 366 | <cffunction name="initPool" access="private" output="false" returntype="void" hint="Initialize and set the internal object Pool"> |
|---|
| 367 | <cfscript> |
|---|
| 368 | instance.objectPool = CreateObject("component","objectPool").init(); |
|---|
| 369 | </cfscript> |
|---|
| 370 | </cffunction> |
|---|
| 371 | |
|---|
| 372 | <!--- ************************************************************* ---> |
|---|
| 373 | |
|---|
| 374 | <cffunction name="ThresholdChecks" access="private" output="false" returntype="boolean" hint="JVM Threshold checks"> |
|---|
| 375 | <cfset var check = true> |
|---|
| 376 | <cfset var jvmThreshold = 0> |
|---|
| 377 | <cfset var jvmFreeMemory = ""> |
|---|
| 378 | <cfset var jvmTotalMemory = ""> |
|---|
| 379 | <cfset var ccBean = getCacheConfigBean()> |
|---|
| 380 | <cftry> |
|---|
| 381 | <!--- Checks ---> |
|---|
| 382 | <cfif ccBean.getCacheFreeMemoryPercentageThreshold() neq 0> |
|---|
| 383 | <cfset jvmFreeMemory = getJavaRuntime().getRuntime().freeMemory()> |
|---|
| 384 | <cfset jvmTotalMemory = getJavaRuntime().getRuntime().totalMemory()> |
|---|
| 385 | <cfset jvmThreshold = ((jvmFreeMemory/jvmTotalMemory)*100)> |
|---|
| 386 | <cfset check = ccBean.getCacheFreeMemoryPercentageThreshold() lt jvmThreshold> |
|---|
| 387 | </cfif> |
|---|
| 388 | <cfcatch type="any"> |
|---|
| 389 | <cfset check = true> |
|---|
| 390 | </cfcatch> |
|---|
| 391 | </cftry> |
|---|
| 392 | <cfreturn check> |
|---|
| 393 | </cffunction> |
|---|
| 394 | |
|---|
| 395 | <!--- ************************************************************* ---> |
|---|
| 396 | |
|---|
| 397 | </cfcomponent> |
|---|