PageRenderTime 105ms CodeModel.GetById 54ms app.highlight 5ms RepoModel.GetById 23ms app.codeStats 0ms

/wheels/vendor/memcached/com/Memcached.cfc

http://cfwheels.googlecode.com/
ColdFusion CFScript | 553 lines | 459 code | 42 blank | 52 comment | 34 complexity | 4755f7d879609a3be7da2a2ac0c5f506 MD5 | raw file
  1<cfcomponent  extends="_base"  output="false">
  2
  3	<!-----------
  4		This memcached client uses the java memcached client written by Dustin Sallings found at:
  5			http://bleu.west.spy.net/~dustin/projects/memcached/
  6		it also borrows from the javaloader project put together by mark mandel
  7			http://www.transfer-orm.com/?action=javaloader.index
  8		This project was also inspired by the previous memcached client put together by shayne sweeney
  9			http://memcached.riaforge.org/
 10		
 11		you can find this project at 
 12			http://cfmemcached.riaforge.org/
 13		And if you feel like visiting my personal blog (Jon Hirschi), it can be found at:
 14			http://www.flexablecoder.com
 15		Wow, that's a mouthful...  
 16		
 17	------------->
 18
 19	<cfset variables.memcached = "">
 20	<cfset variables.timeUnit = "">
 21	<cfset variables.FutureTask = "">
 22	<cfset variables.transcoder = "">
 23	<cfset variables.addrUtil = "">
 24	
 25	<cffunction name="init" displayname="init" hint="init function" access="public" output="false" returntype="any">
 26		
 27		<cfargument name="servers" type="string" required="true" />
 28		<cfscript>
 29			/*
 30				if you want to use the java loader you don't have to do anything, just put this into a location under
 31				your web root and it will load all the classes for you.  if you don't want to use the java loader, 
 32				just comment out the classes below and comment in the lines that are commented out. if you don't want
 33				to use the java loader, then you will need to put the classes somewhere on the class path.
 34				generally an easy place to put this is in the <cfroot>/lib directory.
 35			*/
 36			// if you are having problems loading the memcached client or
 37			// if it is giving you errors saying that it can't find one of 
 38			// the java classes, then you can change this value here to point to the directory
 39			// where the java classes are located. in this case they are in the lib directory,
 40			// just below the directory where this memcached client is placed.  if you have 
 41			// copied the java libs into the java class path, then you can change this to 
 42			// an empty directory.
 43			var MemcachedClientJavaJarDir = "#GetDirectoryFromPath(GetCurrentTemplatePath())#lib";
 44			// these lines to use if you want to use the java loader
 45			var facadeFactory = createObject("component", "facade.FacadeFactory").init();
 46			var javaLoader = createObject("component", "util.JavaLoader").init(facadeFactory.getServerFacade(),MemcachedClientJavaJarDir);			
 47			variables.addrUtil = javaLoader.create("net.spy.memcached.AddrUtil").init();
 48			variables.memcached = javaLoader.create("net.spy.memcached.MemcachedClient").init(addrUtil.getAddresses(arguments.servers));
 49			/*
 50			// these lines to use if you don't want to use the java loader
 51			variables.addrUtil = createObject("java","net.spy.memcached.AddrUtil").init();
 52			variables.memcached = createObject("java","net.spy.memcached.MemcachedClient").init(addrUtil.getAddresses(arguments.servers));
 53			variables.timeUnit = createObject("java","java.util.concurrent.TimeUnit");
 54			*/
 55				
 56			variables.transcoder = variables.memcached.getTranscoder();
 57	
 58			/****************************
 59				some methods return a future object (especially asyncronous sets and gets)
 60				these let you check back in on the result.  they don't require that you wait around for 
 61				a response
 62				
 63				future object methods:
 64					Cancel() - returns a boolean - allows you to cancel the operation
 65					get( int, variables.timeunit ) - returns object - when an interger is sent in, you can 
 66							get the result in that amount of time.
 67					get() - returns an object - get the result when it is available.
 68					isCancelled() -  returns boolean - lets you know if the operation was cancelled
 69					isDone() - returns boolean - returns true when the operation has finished.
 70			
 71				Just a little caveat here.  keys are case sensitive, so make sure that you have the right case...
 72			*/
 73			super.init();
 74		</cfscript>
 75		<cfreturn this>
 76	</cffunction>
 77
 78	<cffunction name="add" displayname="add" access="public" output="false" returntype="any"
 79			hint="Add an object to the cache iff it does not exist already. 
 80				Add an object to the cache iff it does not exist already. 
 81				The exp value is passed along to memcached exactly as given, and will be processed per 
 82				the memcached protocol specification: 
 83
 84				The actual value sent may either be Unix time 
 85				(number of seconds since January 1, 1970, as a 32-bit value), 
 86				or a number of seconds starting from current time. 
 87				In the latter case, this number of seconds may not 
 88				exceed 60*60*24*30 (number of seconds in 30 days); 
 89				if the number sent by a client is larger than that, the server will consider it to be 
 90				real Unix time value rather than an offset from current time. 
 91				
 92				RETURNS: a future representing the processing of this operation
 93
 94			" >
 95		<cfargument name="key" type="string" hint="a single key to add" required="true" />
 96		<cfargument name="value" type="any" hint="the value to set" required="true" />
 97		<cfargument name="expiry" type="numeric" hint="number of seconds until expire" default="0" required="true" />
 98		<cfscript>
 99			var futureTask = "";
100			var ret = "";
101			try {
102				ret = variables.memcached.add(arguments.key, arguments.expiry, serialize(arguments.value) );
103			} catch (Any e)	{
104				// failing gracefully
105			}
106			futureTask = createObject("component","FutureTask").init(ret);
107		</cfscript>
108		<cfreturn futureTask/>
109	</cffunction>
110
111	<cffunction name="asyncGet" access="public" output="false" returntype="Any"
112		hint="Get the given key asynchronously. returns the future value of those keys
113			what you get back wht you use this function is an object that has the future value
114			of the key you asked to retrieve.  You can check at anytime to see if the value has been
115			retrieved by using the  ret.isDone() method. once you get a true from that value, you 
116			need to then call the ret.get() function.  
117			">
118		<cfargument name="key" type="string" hint="given a key, get the key asnycronously" required="true" />
119		<cfscript>
120			var ret = "";
121			var futureTask ="";
122			// gotta go through all this to catch the nulls.
123			try	{
124				ret = variables.memcached.asyncGet(arguments.key);
125				// additional processing might be required.
126			} catch(Any e)	{
127				// failing gracefully
128			}
129			futureTask = createObject("component","FutureTask").init(ret);
130		</cfscript>
131		<cfreturn futureTask/>
132	</cffunction>
133
134	<cffunction name="asyncGetBulk" access="public" output="false" returntype="any"
135			hint="Asynchronously get a bunch of objects from the cache. returns the future value of those keys.  
136				" >
137		<cfargument name="keys" type="array" hint="given a struct of keys, get the keys asnycronously" required="true" />
138		<cfscript>
139			var ret = "";
140			var futureTask = "";
141			// gotta go through all this to catch the nulls.
142			try	{
143				ret = variables.memcached.asyncGetBulk(arguments.keys);
144				// additional processing might be required.
145			} catch(Any e)	{
146				// catch is here to fail gracefully
147			}
148			futureTask = createObject("component","FutureTask").init(ret);
149		</cfscript>
150		<cfreturn futureTask/>
151	</cffunction>
152
153	<cffunction name="decr" displayname="decr" access="public" output="false" returntype="Numeric"
154			hint="Decrement the given key by the given value. returns the new value, or -1 if we were unable to decrement or add" >
155		<cfargument name="key" type="string" hint="a single key to add" required="true" />
156		<cfargument name="by" type="numeric" hint="amount to decrement by" default="0" required="false" />
157		<cfargument name="value" type="numeric" hint="the value to set" required="false" default="0"/>
158		<cfscript>
159			var ret = -1;
160			try {
161				if ( structkeyExists(arguments,"value") )	{
162					ret = variables.memcached.decr(arguments.key,arguments.by,arguments.value);
163				} else {
164					ret = variables.memcached.decr(arguments.key,arguments.by);
165				}
166			}  catch (Any e)	{
167				// catch is here to fail gracefully
168				ret = -1;
169			}
170		</cfscript>
171		<cfreturn ret/>
172	</cffunction>
173
174	<cffunction name="delete" access="public" output="true" returntype="any"
175		hint="Shortcut to delete that will immediately delete the item from the cache. 
176			or in the delay specified. returns a future object that allows you to 
177			check back on the processing further if you choose." >
178		<cfargument name="deletekey" type="string" required="true" hint="the key to delete"/>
179		<cfargument name="delay" type="numeric" required="false" default="0" hint="when to delete the key - time in seconds" />
180		<Cfscript>
181			var ret = false;
182			var futureTask = "";
183			try 	{
184				if (arguments.delay gt 0)	{
185					ret = variables.memcached.delete(arguments.deletekey,arguments.delay);
186				} else {
187					ret = variables.memcached.delete(arguments.deletekey);
188				}
189			}  catch (Any e)	{
190				// failing gracefully
191				ret = "";
192			}
193			if (isdefined("ret"))	{
194				futureTask = createobject("component","FutureTask").init(ret);
195			}  else {
196				futureTask = createobject("component","FutureTask").init();
197			}
198		</Cfscript>
199		<cfreturn futureTask/>
200	</cffunction>
201
202	<cffunction name="get" access="public" output="false" returntype="Any"
203		hint=" Get with a single key.  waits for a return value" >
204		<cfargument name="key" type="string" required="true" hint="given a key, get the key asnycronously" />
205		<cfargument name="timeout" type="numeric" required="false" default="#variables.defaultRequestTimeout#" 
206				hint="the number of milliseconds to wait until for the response.  
207				a timeout setting of 0 will wait forever for a response from the server"/>
208		<cfargument name="timeoutUnit" type="numeric" required="false" default="#variables.defaultTimeoutUnit#"
209				hint="The timeout unit to use for the timeout"/>
210		<cfscript>
211			var ret = "";
212			var futureTask = "";
213			// gotta go through all this to catch the nulls.
214			try 	{
215				ret = variables.memcached.asyncGet(arguments.key);	
216				if (not isdefined("ret") )	{
217					ret = "";
218				} else {
219					futureTask = createObject("component","FutureTask").init(ret);
220					ret = futureTask.get(arguments.timeout,arguments.timeoutUnit);
221				}
222			} catch	(Any e) 	{
223				// failing gracefully
224			}
225		</cfscript>
226		<cfreturn ret/>
227	</cffunction>
228
229	<cffunction name="getBulk" access="public" output="false" returntype="any"
230		hint="Get the values for multiple keys from the cache. waits for a return value" >
231		<cfargument name="keys" type="array" hint="given a key, get the key asnycronously" required="true" />
232		<cfargument name="timeout" type="numeric" required="false" default="#variables.defaultRequestTimeout#" 
233				hint="the number of milliseconds to wait until for the response.  
234				a timeout setting of 0 will wait forever for a response from the server"/>
235		<cfargument name="timeoutUnit" type="numeric" required="false" default="#variables.defaultTimeoutUnit#"
236				hint="The timeout unit to use for the timeout"/>
237		<cfscript>
238			var ret = "";
239			var futureTask = "";
240			// gotta go through all this to catch the nulls.
241			try {
242				ret = variables.memcached.asyncGetBulk(arguments.keys);
243				if (not isdefined("ret") )	{
244					ret = "";
245				} else {
246					futureTask = createObject("component","FutureTask").init(ret);
247					ret = futureTask.get(arguments.timeout,arguments.timeoutUnit);
248				}
249			}  catch (Any e)	{
250				// failing gracefully
251				ret = "";
252			}
253		</cfscript>
254		<cfreturn ret/>
255	</cffunction>
256
257	<cffunction name="incr" access="public" output="false" returntype="Numeric"
258		hint="Increment the given key by the given amount. returns -1 if string cannot be incremented">
259		<cfargument name="key" type="string" hint="a single key to add" required="true" />
260		<cfargument name="by" type="numeric" hint="amount to increment by" default="0" required="false" />
261		<cfargument name="value" type="numeric" hint="the default value used if null" required="false"/>
262		<cfscript>
263			var ret = -1;
264			
265			try 	{
266				if ( structkeyExists(arguments,"value") )	{
267					ret = variables.memcached.incr(arguments.key,arguments.by,arguments.value);
268				} else {
269					ret = variables.memcached.incr(arguments.key,arguments.by);
270				}
271			} catch (Any e)	{
272				// failing gracefully here
273				ret = -1;
274			}
275		</cfscript>
276		<cfreturn ret/>
277	</cffunction>
278
279	<cffunction name="doReplace" access="public" output="false" returntype="any"
280		hint="Replace an object with the given value iff there is already a value for the given key.
281			
282			The exp value is passed along to memcached exactly as given, 
283			and will be processed per the memcached protocol specification: 
284
285			The actual value sent may either be Unix time (number of seconds since January 1, 1970, 
286			as a 32-bit value), or a number of seconds starting from current time. 
287			In the latter case, this number of seconds may not exceed 60*60*24*30 
288			(number of seconds in 30 days); if the number sent by a client is larger than that, 
289			the server will consider it to be real Unix time value rather than an offset from current time.
290		
291			RETURNS: a future representing the processing of the operation		
292		" >
293		<cfargument name="key" type="string" hint="a single key to add" required="true" />
294		<cfargument name="value" type="any" hint="the value to set" required="true" />
295		<cfargument name="expiry" type="numeric" hint="number of seconds until expire" default="0" required="true"/>
296		<cfscript>
297			var futureTask = "";
298			var ret = "";
299			try	{
300				ret = variables.memcached.replace(arguments.key,arguments.expiry,serialize(arguments.value));
301			} catch (Any e)	{
302				// failing gracefully
303				ret = "";
304			}
305			futureTask = createObject("component","FutureTask").init(ret);
306		</cfscript>
307		<cfreturn futureTask/>
308	</cffunction>
309
310	<cffunction name="set" access="public" output="false" returntype="any"
311		hint="Set an object in the cache regardless of any existing value.
312			Set an object in the cache regardless of any existing value. 
313			The exp value is passed along to memcached exactly as given, 
314			and will be processed per the memcached protocol specification: 
315
316			The actual value sent may either be Unix time (number of seconds since January 1, 1970, 
317			as a 32-bit value), or a number of seconds starting from current time. 
318			In the latter case, this number of seconds may not exceed 60*60*24*30 
319			(number of seconds in 30 days); if the number sent by a client is larger than that, 
320			the server will consider it to be real Unix time value rather than an offset from current time.
321		
322			RETURNS: a future Task representing the processing of the operation
323		" >
324		<cfargument name="key" type="string" hint="a single key to add" required="true" />
325		<cfargument name="value" type="any" hint="the value to set" required="true" />
326		<cfargument name="expiry" type="numeric" hint="number of seconds until expire" default="0" required="true"/>
327		<cfscript>
328			var futureTask = "";
329			var ret = "";
330			try 	{
331				ret = variables.memcached.set(arguments.key,arguments.expiry,serialize(arguments.value));
332			}  catch (Any e)	{
333				// failing gracefully
334				ret = "";
335			}
336			futureTask = createObject("component","FutureTask").init(ret);
337		</cfscript>
338		<cfreturn futureTask/>
339	</cffunction>
340
341	<!------------------ public util functions ------------------>
342	
343	<cffunction name="concatArrayQueries" access="public" returntype="any" output="false"
344		hint="this will return either full query or an empty string if no queries are found.
345			in the case of sending in an array full of null values, it will return an empty string" >
346		<cfargument name="arrQueries" required="true" type="array">
347		<cfscript>
348			var mainQuery = "";
349			var arrColumns = "";
350			var ColumnLength = "";
351			var i = 1;
352			var j = 1;
353			var currentRow = 1;
354			
355			for (i=1;i lte arrayLen(arrQueries);i=i+1)	{
356				if (isQuery(arrQueries[i]) and isQuery(mainQuery))	{
357					currentRow = queryAddRow(mainQuery,1);
358					for (j=1; j lte columnLength;j=j+1)	{
359						mainQuery[arrColumns[j]][currentRow] = arrQueries[i][arrColumns[j]][1];
360					}
361				} else if ( isQuery(arrQueries[i]) )	{
362					mainQuery = duplicate(arrQueries[i]);
363					arrColumns = listToArray(mainQuery.columnList);
364					ColumnLength = arrayLen(arrColumns);
365				}
366			}
367		</cfscript>
368		<cfreturn mainQuery>
369	</cffunction>
370	
371	<cffunction name="concatStructQueries" access="public" returntype="any" output="false" 
372		hint="this will return either full query or an empty string if no queries are found.
373			in the case of sending in an array full of null values, it will return an empty string" >
374		<cfargument name="structQueries" required="true" type="struct">
375		<cfscript>
376			var arrKeys = listtoarray(structKeyList(structQueries));
377			var mainQuery = "";
378			var arrColumns = "";
379			var ColumnLength = "";
380			var i = 1;
381			var j = 1;
382			var currentRow = 1;
383			
384			for (i=1;i lte arrayLen(arrKeys);i=i+1)	{
385				if (isQuery(structQueries[arrKeys[i]]) and isQuery(mainQuery))	{
386					currentRow = queryAddRow(mainQuery,1);
387					for (j=1; j lte columnLength;j=j+1)	{
388						mainQuery[arrColumns[j]][currentRow] = structQueries[arrKeys[i]][arrColumns[j]][1];
389					}
390				} else if ( isQuery(structQueries[arrKeys[i]]) )	{
391					mainQuery = duplicate(structQueries[arrKeys[i]]);
392					arrColumns = listToArray(mainQuery.columnList);
393					ColumnLength = arrayLen(arrColumns);
394				}
395			}
396		</cfscript>
397		<cfreturn mainQuery>
398	</cffunction>
399	
400	
401	<Cffunction name="concatQueries" access="public" output="false" returntype="query"
402			hint="queries need to be exactly the same">
403		<cfargument name="query1" type="query" required="true">
404		<cfargument name="query2" type="query" required="true">
405		<cfargument name="arrColumns" type="array" required="false" default="#listToArray(Query1.columnList)#">
406		<Cfscript>
407			var columnLength = arrayLen(arrColumns);
408			var currentRow = queryAddRow(query1,1);
409			for (i=1; i lte query2.recordcount;i=i+1)	{
410				for (j=1; j lte columnLength;j=j+1)	{
411					query1[arrColumns[j]][currentRow] = query2[arrColumns[j]][i];
412				}
413			}
414		</Cfscript>
415		<cfreturn query1>
416	</Cffunction>
417	
418	<cffunction name="createMemcachedKey" returnType="string" output="no" access="public" 
419		hint="create a key from a struct of input arguments">
420		<cfargument name="keyStruct" type="struct" required="true">
421		<cfargument name="prefix" type="string" required="false" default="">	
422		<cfset var cacheKey = "">
423		<cfset var arrKeys = structkeyArray(arguments.keyStruct)>
424		<cfset arraySort(arrKeys,"textnocase")>
425		<cfloop from="1" to="#arrayLen(arrKeys)#" index="i">
426			<cfset cacheKey = cacheKey.concat("#arrKeys[i]#=#arguments.keyStruct[arrKeys[i]]#")>
427		</cfloop>
428		<cfreturn arguments.prefix.concat(hash(cacheKey))>
429	</cffunction>
430
431
432	<!-------------------- admin functions ----------------------->
433
434	<cffunction name="getTranscoder" access="public" output="false" returntype="Any"
435			hint="Get the current transcoder that's in use." >
436		<cfreturn variables.memcached.getTranscoder()/>
437	</cffunction>
438
439	<cffunction name="setTranscoder" access="public" output="false" returntype="void"
440		hint="Set the transcoder for managing the cache representations of objects going in and out of the cache." >
441		<cfargument name="transcoder" required="true" type="any" hint="Must be a transcoder object">
442		<cfset variables.memcached.setTranscoder(arguments.transcoder)>
443	</cffunction>
444
445	<cffunction name="flush" access="public" output="false" returntype="any"
446		hint="Flush all caches from all servers immediately or with a delay which is provided." >
447		<cfargument name="delay" type="numeric" required="false" default="0" hint="Integer value - time in seconds to delay flushing cache">
448		<cfscript>
449			var ret = true;
450			if (arguments.delay gt 0)	{
451				ret = variables.memcached.flush(arguments.delay);
452			} else {
453				ret = variables.memcached.flush();
454			}
455		</cfscript>
456		<cfreturn ret/>
457	</cffunction>
458
459	<cffunction name="run" access="public" output="true" returntype="void"
460		hint="Infinitely loop processing IO. what can we use this for?" >
461		<cfreturn variables.memcached.run()/>
462	</cffunction>
463
464	<cffunction name="shutdown" access="public" output="false" returntype="boolean"
465		hint="Shut down this client.">
466		<cfargument name="timeout" required="false" type="numeric" default="0" hint="Integer number of units which to wait for the queue to die down">
467		<cfargument name="timeUnit" required="false" type="any" hint="a time unit object - java.util.concurrent.TimeUnit">
468		<cfscript>
469			var ret = true;
470			if (arguments.timeout gt 0)	{
471				ret = variables.memcached.shutdown(arguments.timeout,arguments.timeUnit);
472			} else {
473				ret = variables.memcached.shutdown();
474			}
475		</cfscript>
476		<cfreturn ret/>
477	</cffunction>
478
479	<cffunction name="waitForQueues" access="public" output="false" returntype="boolean"
480		hint="Wait for the queues to die down." >
481		<cfargument name="timeout" required="true" type="numeric" default="0" hint="Integer number of units which to wait for the queue to die down">
482		<cfargument name="timeUnit" required="true" type="any" default="#variables.timeUnit#" hint="a time unit object ">
483		<cfscript>
484			var ret = true;
485			ret = variables.memcached.waitForQueues(arguments.timeout,arguments.timeUnit);
486		</cfscript>
487		<cfreturn ret/>
488	</cffunction>
489
490	<cffunction name="getVersions" access="public" output="false" returntype="struct"
491		hint="Get the versions of all of the connected memcacheds. struct comes back as ">
492		<!----------- there is a problem in here that if memcached is not running, this will hang 
493			this is a known problem with the underlying java client - which should be addressed soon.
494		------>
495		<cfset var myVersions = variables.memcached.getVersions()>
496		<cfset var ret = StructNew()>
497		<cfif isdefined("myVersions")>
498			<!------- checking isdefined here, just incase we get a java null back ---->
499			<cfset ret = toStruct(myVersions)>
500		</cfif>
501		<cfreturn ret/>
502	</cffunction>
503	
504	<cffunction name="getStats" access="public" output="false" returntype="any"
505			hint="get all of the stats from all of the connections">
506		<!----------- there is a problem in here that if memcached is not running, this will hang 
507			this is a known problem with the underlying java client which should be addressed soon.
508		------>
509		<cfset var myStats = variables.memcached.getStats()>
510		<cfset var ret = StructNew()>
511		<cfif isdefined("myStats")>
512			<!------- checking isdefined here, just incase we get a java null back ---->
513			<cfset ret = toStruct(myStats)>
514		</cfif>
515		<cfreturn ret/>
516	</cffunction>
517	
518	<cffunction name="stats" access="public" output="false" returntype="any"
519			hint="get all of the stats from all of the connections">
520		<!----------- there is a problem in here that if memcached is not running, this will hang 
521			this is a known problem with the underlying java client which should be addressed soon.
522		
523			this function here for compatability with other memcached client.
524		------>
525		<cfreturn getStats()/>
526	</cffunction>
527	<!------------------ private util functions ------------------>
528	
529	<cffunction name="dateAddSeconds" access="private" output="false" returntype="date"
530		hint="Converts a given number (seconds) to a future date-time.">
531		<cfargument name="seconds" required="true" />
532		<cfreturn DateAdd("s", arguments.seconds, Now()) />
533	</cffunction>
534			
535	<cffunction name="toStruct" returntype="struct" access="private" output="false" hint="converts a java hashmap into a usable struct">
536		<cfargument name="hashMap" type="Any" required="true"> 
537		<!------- thanks to Ken Kolodziej for this snippet of code.  it works great! -------->
538		<cfscript>
539			var theStruct = structNew();
540			var key = "";
541			var newStructKey = ""; 
542			var keys = arguments.hashMap.keySet();
543			var iter = keys.Iterator();
544			
545			while(iter.HasNext()) {
546				key = iter.Next();
547				newStructKey = key.toString();
548				theStruct[newStructKey] = arguments.hashMap.get(key);
549			}
550		</cfscript>
551		<cfreturn theStruct>
552	</cffunction>
553</cfcomponent>