Hi Guys I have managed to find alternative ways t...
# lucee
c
Hi Guys I have managed to find alternative ways to use the following ACF cache BIFs, in Lucee:
Copy code
cacheRegionNew()
cacheRegionExists()
Because, a web admin password would be required in Lucee, otherwise. Does anyone know a way to emulate the functionality of:
Copy code
cacheRegionRemove()
https://docs.lucee.org/reference/functions/cacheregionremove.html In Lucee, because this is the third & final method that requires a web admin password? I don't want to use the administrator GUI. This needs to be done programmatically? I was thinking perhaps something along the lines of:
Copy code
variables.szCacheName = "CONFIGSETTINGS";
variables.szApplicationName = "LA-EPORTFOLIO";
variables.szPropertiesCachename = ucase(variables.szApplicationName) & "_" & ucase(variables.szCacheName);
variables.stuCacheProps = {
	CLEARONFLUSH = true,
	DISKPERSISTENT = true,
	ETERNAL = true,
	MAXELEMENTSINMEMORY = 1000000,
	MAXELEMENTSONDISK = 0,
	OBJECTTYPE = "OBJECT",
	OVERFLOWTODISK = true,
	STATISTICS = true,
	TIMETOLIVESECONDS = 0,
	TIMETOIDLESECONDS = 0
};

variables.cacheExists = StructKeyExists(this,"cache") AND StructKeyExists(this.cache,"connections") AND StructKeyExists(this.cache.connections,variables.szPropertiesCachename) ? true : false;

if(NOT variables.cacheExists){
	this.cache.connections[variables.szPropertiesCachename] = {
		class: 'org.lucee.extension.cache.eh.EHCache',
		custom: variables.stuCacheProps,
		default: 'object'
	};
	this.cache.object = 'default';
}

variables.isDeleted = StructDelete( this.cache.connections, variables.szPropertiesCachename, true );
But, would this actually destroy the cache connection?
z
something like
<cfapplication action="update" caches={what ever}>
šŸ‘ 1
c
So maybe:
Copy code
<cfapplication action="update" cache="#{
    connections: {
        '#variables.szPropertiesCachename#': null
    }
}#">
https://docs.lucee.org/guides/cookbooks/application-context-update.html Do you think I can use the
<cfapplication />
tag anywhere? I mean like inside a CFC or template?
Or maybe I can use a script based version, like:
Copy code
cfapplication(action="update", cache={
    connections[variables.szPropertiesCachename]: null
});
z
yeah or
application action="update" etc
šŸ‘ 1
c
https://docs.lucee.org/reference/unsupported.html
Copy code
Functions

The following functions, from Adobe CFML, are not supported by Lucee:

cacheregionexists
cacheregionnew
cacheregionremove
…
Is this true? The reason I ask, is I am having real trouble trying to get:
Copy code
cacheRegionExists()
cacheRegionRemove()
To work? I mean I can initialise a cache in Lucee, but when I try and use:
Copy code
cacheRegionExists(cacheName, webAdminPassword)
It always return false? šŸ¤”
But, there are docs for each of these methods: https://docs.lucee.org/reference/functions/cacheregionnew.html https://docs.lucee.org/reference/functions/cacheregionexists.html https://docs.lucee.org/reference/functions/cacheregionremove.html I am presuming that: https://docs.lucee.org/reference/unsupported.html This is an an old document? Anyway, for
CacheRegionExists()
, I have written this:
Copy code
<cffunction name="CacheRegionExists" access="public" returntype="boolean" output="false" hint="Does the cache region exist in Lucee.">
    <cfargument name="cacheName" default="" />
    <cfset var local = {} />
    <cfset local.cacheRegionExists = false />
    <cfset local.cacheNamesProperties = CacheGetProperties(arguments.cacheName) />
    <cfif IsArray(local.cacheNamesProperties) AND ArrayLen(local.cacheNamesProperties) AND IsStruct(local.cacheNamesProperties[1]) AND StructKeyExists(local.cacheNamesProperties[1], "name") AND CompareNoCase(local.cacheNamesProperties[1].name, arguments.cacheName) EQ 0>
        <cfset local.cacheRegionExists = true />
    </cfif>
    <cfreturn local.cacheRegionExists />
</cffunction>
I am just not 100% sure that this is robust enough? I have even added the exact same Cache in the Administrator GUI and the BIF CacheRegionExists(), still doesn't return true?
cache-lucee-1.png
z
that doc is manually compiled and out of date https://docs.lucee.org/reference/unsupported.html
šŸ‘ 1
c
Cheers for that
z
feel free to click the github icon and submit any changes required
c
I have submitted the changes...šŸ™‚
z
thanks, can you fix the typo passwaord and sign the cla
šŸ‘ 1
c
OK. There is definitely a bug in:
Copy code
CacheRegionExists()
In ACF it works perfectly, but in Lucee, it always returns false. I am happy to write a bug report šŸ™‚ Do you have the link for bug reports in Lucee. Thanks...
z
cacheRegionExists only exists for backwards compat and doesn't look into the application caches
šŸ™Œ 1
šŸ‘ 1
c
Actually, I have used:
Copy code
GetApplicationSettings()
So, that's great. It means I was moving in the right direction! šŸ‘Š
z
cool
šŸ™ 1
the output for that function is manually rebuilt, so if you find any inconsistencies let me know
the bug tracker is here https://luceeserver.atlassian.net/ always ask here or on dev.lucee.org before filing bugs please
šŸ‘ 1
c
That leaves me with one final question:
Copy code
How do we remove cache regions in Lucee?
I have tried deleting the
cache
property from
GetApplicationSettings()
, but I have a feeling this is a read-only struct? Can you think of anyway, we can emulate ACFs
CacheRegionRemove()
, in Lucee? I did try:
Copy code
<cfapplication 
	action="update" 
	caches=""
/>
But this had no effect?
z
take the value from
getApplicationSettings
and do an
application action=update cache=<value from get application settings>
šŸ™Œ 1
alas, there's the odd edge case with setting to "", lucee often ignores an empty attribute/value
šŸ‘ 1
c
Wait a second. If you get the value from:
Copy code
GetApplicationSettings()
Like:
Copy code
<cffunction name="GetCacheLuceeApplication" access="public" returntype="any" output="false" hint="Get cache region in Lucee, using application struct">
    <cfargument name="szPropertiesCachename" default="" />
    <cfset var local = {} />
    <cfset local.cacheConnection = {} />
    <cfset local.applicationSettings = GetApplicationSettings() />
    <cfif StructKeyExists(local.applicationSettings, "cache") AND StructKeyExists(local.applicationSettings.cache, "connections") AND StructKeyExists(local.applicationSettings.cache.connections, arguments.szPropertiesCachename)>
        <cfset local.cacheConnection = local.applicationSettings.cache.connections[arguments.szPropertiesCachename] />
    </cfif>
    <cfreturn local.cacheConnection />
</cffunction>

<cfapplication action="update" cache="#GetCacheLuceeApplication('someCacheName')#" />
Doesn't that update the application's cache with the current value? How does this delete the cache? Unfortunately, there is no:
Copy code
<cfapplication action="delete" cache="#GetCacheLuceeApplication('someCacheName')#" />
I tried doing:
Copy code
<cfapplication action="update" cache="null" />
And, again, this failed to work?
z
you pass in the whole definition for all caches, I.e minus the one you want to delete
šŸ‘ 1
c
Ahhhh. I get you. Nice one...šŸ™
Hmmm. I have tried this and it doesn't remove the connection. Even, if I just add an empty Struct to the application cache value, nothing happens. It seems like the cache cannot be updated programmatically, in Lucee?
z
it's an application.cfc cache definition you are trying to remove in this manner?
c
Yes
Application.cfc
Copy code
<cfcomponent output="false" displayname="Application">

	<cfscript>

		variables.nSessionTimeout = CreateTimeSpan(0, 0, 30, 0);
		request.szSessionTimeoutSecs = DateDiff("s", 0, variables.nSessionTimeout);

		variables.nServerName = "lucee539166";

		this.name = variables.nServerName;
		this.applicationTimeout = CreateTimeSpan(30, 0, 0, 0);
		this.serverSideFormValidation = true;
		this.sessionManagement = true;
		this.setClientCookies = true;
		this.scriptProtect = "all";
		this.timeout = 60;
		this.sessionTimeout = variables.nSessionTimeout;
		this.clientManagement = true;
		this.clientStorage = "cookie";
		this.loginStorage = "session";

		variables.szCacheName = "CONFIGSETTINGS";
		variables.szApplicationName = "LA-EPORTFOLIO";
		variables.szPropertiesCachename = ucase(variables.szApplicationName) & "_" & ucase(variables.szCacheName);
		variables.stuCacheProps = {
			CLEARONFLUSH = true,
			DISKPERSISTENT = true,
			ETERNAL = true,
			MAXELEMENTSINMEMORY = 1000000,
			MAXELEMENTSONDISK = 0,
			OBJECTTYPE = "OBJECT",
			OVERFLOWTODISK = true,
			STATISTICS = true,
			TIMETOLIVESECONDS = 0,
			TIMETOIDLESECONDS = 0
		};

		variables.cacheExists = StructKeyExists(this,"cache") AND StructKeyExists(this.cache,"connections") AND StructKeyExists(this.cache.connections,variables.szPropertiesCachename) ? true : false;

		if(NOT variables.cacheExists){
			this.cache.connections[variables.szPropertiesCachename] = {
				class: 'org.lucee.extension.cache.eh.EHCache',
				custom: variables.stuCacheProps,
				default: 'object'
			};
			this.cache.object = 'default';
		}

	</cfscript>

	<cffunction name="onApplicationEnd" access="public" output="false" returnType="void">
		<cfargument name="applicationScope" type="any" required="true" />
		<cfreturn true />
	</cffunction>

	<cffunction name="onApplicationStart" access="public" output="false" returnType="boolean">
		<cfscript>
			application.szCache = variables.cache;
			return true;
		</cfscript>
	</cffunction>

	<cffunction name="onRequestEnd" access="public" output="false" returntype="boolean">
		<cfargument name="targetPage" type="string" required="true" />
		<cfreturn true />
	</cffunction>

	<cffunction name="onRequestStart" access="public" output="false" returntype="boolean">
		<cfargument name="targetPage" type="string" required="true" />
		<cfreturn true />
	</cffunction>

	<cffunction name="onRequest">
		<cfargument name="targetPage" type="String" required="true" />
		<cfscript>
			request.nServerName = variables.nServerName;
			request.szPropertiesCachename = variables.szPropertiesCachename;
			request.szCache = variables.szCache;
			if(!(StructKeyExists(application, "szCache")) OR StructKeyExists(url, "appreload")){
				application.szCache = variables.szCache;
			}
		</cfscript>
		<cfinclude template="#arguments.targetPage#">
	</cffunction>

</cfcomponent>
And then once it has been created, I try this in a template. These methods are actually in components, but I have put everything on one page, for this demo: index.cfm
Copy code
<cffunction name="CacheApplicationExistsLucee" access="public" returntype="boolean" output="false" hint="Does the application cache region exist in Lucee">
    <cfargument name="szPropertiesCachename" default="" />
    <cfset var local = {} />
    <cfset local.szPropertiesCachename = arguments.szPropertiesCachename />
    <cfset local.cacheApplicationExists = false />
    <cftry>
        <cfset local.applicationSettings = GetApplicationSettings() />
	    <cfif StructKeyExists(local.applicationSettings, "cache") AND StructKeyExists(local.applicationSettings.cache, "connections") AND StructKeyExists(local.applicationSettings.cache.connections, arguments.szPropertiesCachename)>
            <cfset local.cacheApplicationExists = true />
        </cfif>
        <cfcatch>
            <cfset local.cacheApplicationExists = true />
        </cfcatch>
    </cftry>
    <cfreturn local.cacheApplicationExists />
</cffunction>

<cffunction name="CacheRegionExistsLucee" access="public" returntype="boolean" output="false" hint="Does the cache region exist in Lucee">
	<cfargument name="szPropertiesCachename" default="" />
	<cfset var local = {} />
	<cfset local.szPropertiesCachename = arguments.szPropertiesCachename />
	<cfset local.cacheRegionExists = false />
	<cftry>
		<cfset local.cacheNamesProperties = CacheGetProperties(local.szPropertiesCachename) />
		<cfif IsArray(local.cacheNamesProperties) AND ArrayLen(local.cacheNamesProperties) AND IsStruct(local.cacheNamesProperties[1]) AND StructKeyExists(local.cacheNamesProperties[1], "name") AND CompareNoCase(local.cacheNamesProperties[1].name, local.szPropertiesCachename) EQ 0>
			<cfset local.cacheRegionExists = true />
		</cfif>
		<cfcatch>
			<cfset local.cacheRegionExists = true />
		</cfcatch>
	</cftry>
	<cfreturn local.cacheRegionExists />
</cffunction>

<cffunction name="CacheRegionRemoveLucee" access="public" returntype="boolean" output="false" hint="Removes the cache region in Lucee">
	<cfargument name="szPropertiesCachename" default="" />
	<cfset var local = {} />
	<cfset local.isDeleted = false />
	<cftry>
		<!--- I have also used CacheApplicationExistsLucee(), but it makes no difference --->
		<cfset local.cacheRegionExists = CacheRegionExistsLucee(arguments.szPropertiesCachename) />
		<cfif local.cacheRegionExists>
			<!--- Note: remove only the named cache connection and the cache object, if it belongs to this cache name --->
			<cfset local.applicationSettings = GetApplicationSettings() />
			<cfset local.isConnectionDeleted = false />
			<cfset local.isConnectionDeleted = StructDelete(local.applicationSettings.cache.connections, arguments.szPropertiesCachename, true) />
			<cfset local.isObjectDeleted = false />
			<cfif StructKeyExists(local.applicationSettings, "cache") AND StructKeyExists(local.applicationSettings.cache, "object") AND CompareNoCase(local.applicationSettings.cache.object, arguments.szPropertiesCachename) EQ 0>
				<cfset local.isObjectDeleted = StructDelete(local.applicationSettings.cache, "object", true) />
			</cfif>
			<cfset local.applicationSettings = GetApplicationSettings() />
			<cftry>
				<cfset local.cacheSegment = local.applicationSettings.cache />
				<cfset local.attributeCollection = {
					action: "update",
					cache: local.cacheSegment
				} />
				<cfapplication attributeCollection="#local.attributeCollection#" />
				<cfcatch>
					<!--- Note: if the section above throws an error, it means that there aren't any other cache connections, which means that the cache value can be an empty struct --->
					<cfset local.cacheEmpty = {} />
					<cfset local.attributeCollection = {
						action: "update",
						cache: local.cacheEmpty
					} />
					<cfapplication attributeCollection="#local.attributeCollection#" />
				</cfcatch>
			</cftry>
			<cftry>
				<cfset local.cacheRegionExists = CacheRegionExistsLucee(arguments.szPropertiesCachename) />
				<cfif NOT local.cacheRegionExists>
					<cfset local.isDeleted = true />
				</cfif>
				<cfcatch>
					<cfset local.isDeleted = true />
				</cfcatch>
			</cftry>
		</cfif>
		<cfcatch></cfcatch>
	</cftry>
	<cfreturn local.isDeleted />
</cffunction>

<cfscript>

	// this function prevents an error being thrown, when there is no such region, after it has been removed in ACF

	function _CacheGetProperties() {
		var local = {};
		local.obj = {};
		try{
			local.obj = CacheGetProperties(request.szPropertiesCachename);
		}
		catch(any e){
		}
		return local.obj;
	}

	variables.obj = {
		"1_cacheGetPropertiesBefore": CacheGetProperties(request.szPropertiesCachename),
		"2_cacheRegionExistsBefore": CacheRegionExistsLucee(request.szPropertiesCachename),
		"3_cacheRegionRemove": CacheRegionRemoveLucee(request.szPropertiesCachename),
		"4_cacheRegionExistsAfter": CacheRegionExistsLucee(request.szPropertiesCachename),
		"5_cacheGetPropertiesAfter": _CacheGetProperties()
	};

	WriteDump(var="#variables.obj#");

</cfscript>