Sending Random SMS Text Messages With ColdFusion To Make Her Feel Loved

<cfcomponent
	output="true"
	hint="Handle the application level events.">
 
 
	<!--- Define application. --->
	<cfset THIS.Name = "SMS Messages {#Hash( GetCurrentTemplatePath() )#}" />
	<cfset THIS.ApplicationTimeout = CreateTimeSpan( 2, 0, 0, 0 ) />
	<cfset THIS.SessionManagement = false />
	<cfset THIS.SetClientCookies = false />
 
 
	<!--- Set page request settings. --->
	<cfsetting
		requesttimeout="20"
		showdebugoutput="false"
		enablecfoutputonly="true"
		/>
 
 
	<cffunction
		name="OnApplicationStart"
		access="public"
		returntype="boolean"
		output="false"
		hint="Fires when the application is first run or manually reset.">
 
		<!--- Define the local scope. --->
		<cfset var LOCAL = StructNew() />
 
 
		<!---
			Since this application might get called
			manually, we cannot depend on a single threaded
			environment. But, we also have to consider the
			environment... what are the chances that two
			concurrent requests might come in? Zero. But, in
			the stride of good practice, I will implement good
			locking proactices anyway.
		--->
		<cflock
			scope="APPLICATION"
			type="EXCLUSIVE"
			timeout="10"
			throwontimeout="true">
 
			<!--- Clear the application scope. --->
			<cfset StructClear( APPLICATION ) />
 
			<!---
				Read in the XML file that contains are
				defines our library of random text messages
				(SMS Messages).
			--->
			<cffile
				action="READ"
				file="#ExpandPath( './messages.xml' )#"
				variable="LOCAL.MessageData"
				/>
 
 
			<!---
				Parse the message data into an application-
				scoped XML data structure.
			--->
			<cfset APPLICATION.MessagesXML = XmlParse(
				LOCAL.MessageData
				) />
 
 
			<!---
				The XML is great, but for our purposes, it
				would be easier to work with a query object
				(we are going to need to query for messages
				based on dynamic criteria). Therefore, we
				are going to convert our XML object into a
				ColdFusion query object.
			--->
			<cfset APPLICATION.Messages = QueryNew(
				"id, message, min_time, max_time, days",
				"INTEGER, VARCHAR, DECIMAL, DECIMAL, VARCHAR"
				) />
 
 
			<!---
				Now, let's get a short hand reference to the
				messages array within our XML document.
			--->
			<cfset LOCAL.Messages = APPLICATION.MessagesXML.Messages.XmlChildren />
 
			<!---
				Loop over the messages and each one of them
				to the message query.
			--->
			<cfloop
				index="LOCAL.MessageIndex"
				from="1"
				to="#ArrayLen( LOCAL.Messages )#"
				step="1">
 
				<!---
					Get a short hand reference to the message
					(XML Node) that we are currently looking at.
				--->
				<cfset LOCAL.Message = LOCAL.Messages[ LOCAL.MessageIndex ] />
 
 
				<!--- Add a new row to the query. --->
				<cfset QueryAddRow( APPLICATION.Messages ) />
 
				<!---
					Add the ID of the message. This is just
					going to be the index of the XML child.
				--->
				<cfset APPLICATION.Messages[ "id" ][ LOCAL.MessageIndex ] = JavaCast( "int", LOCAL.MessageIndex ) />
 
				<!---
					Store the message. Be sure to trim the
					message we the XML data might have white
					space. When sending a SMS text message,
					data efficiency is HIGH priority.
				--->
				<cfset APPLICATION.Messages[ "message" ][ LOCAL.MessageIndex ] = JavaCast( "string", Trim( LOCAL.Message.XmlText ) ) />
 
				<!---
					When storing the min time, check to see
					if the attribute exists and is a valid
					date/time object. If it does not exist,
					then we are going to store a null value.
				--->
				<cfif (
					StructKeyExists( LOCAL.Message.XmlAttributes, "mintime" ) AND
					IsNumericDate( LOCAL.Message.XmlAttributes.mintime )
					)>
 
					<!--- Store the time as a decimal. --->
					<cfset APPLICATION.Messages[ "min_time" ][ LOCAL.MessageIndex ] = JavaCast( "float", LOCAL.Message.XmlAttributes.mintime ) />
 
				<cfelse>
 
					<!---
						The time did not exists. Store a NULL
						value that can be checked in our query
						of queries.
					--->
					<cfset APPLICATION.Messages[ "min_time" ][ LOCAL.MessageIndex ] = JavaCast( "null", 0 ) />
 
				</cfif>
 
				<!---
					When storing the max time, check to see
					if the attribute exists and is a valid
					date/time object. If it does not exist,
					then we are going to store a null value.
				--->
				<cfif (
					StructKeyExists( LOCAL.Message.XmlAttributes, "maxtime" ) AND
					IsNumericDate( LOCAL.Message.XmlAttributes.maxtime )
					)>
 
					<!--- Store the time as a decimal. --->
					<cfset APPLICATION.Messages[ "max_time" ][ LOCAL.MessageIndex ] = JavaCast( "float", LOCAL.Message.XmlAttributes.maxtime ) />
 
				<cfelse>
 
					<!---
						The time did not exists. Store a NULL
						value that can be checked in our query
						of queries.
					--->
					<cfset APPLICATION.Messages[ "max_time" ][ LOCAL.MessageIndex ] = JavaCast( "null", 0 ) />
 
				</cfif>
 
 
				<!---
					When storing the valid days, check to see
					if the value exists. If it does not exist,
					then we are going to store a NULL value.
				--->
				<cfif StructKeyExists( LOCAL.Message.XmlAttributes, "days" )>
 
					<!--- Store the days. --->
					<cfset APPLICATION.Messages[ "days" ][ LOCAL.MessageIndex ] = JavaCast( "string", LCase( LOCAL.Message.XmlAttributes.days ) ) />
 
				<cfelse>
 
					<!---
						Days did not exists. Store a NULL
						value that can be checked in our
						query of queries.
					--->
					<cfset APPLICATION.Messages[ "days" ][ LOCAL.MessageIndex ] = JavaCast( "null", 0 ) />
 
				</cfif>
 
			</cfloop>
 
 
 
			<!---
				In addition to the actual message data, we
				are going to need data about the state of
				the application. Let's set up a default
				data structure value.
			--->
			<cfset APPLICATION.Settings = StructNew() />
 
			<!---
				This is the file path to our application
				settings data file. We will need this to init
				here, but also to update the file later.
			--->
			<cfset APPLICATION.Settings.FilePath = ExpandPath(
				"./app_data.xml"
				) />
 
			<!---
				We don't want to hassle ourselves... I mean we
				want her to feel loved, but come on! Let's only
				remind our selves on a random time that is not
				too short. THis value will be the time at which
				we can next send a message. For default, we
				will use Now() (a new message can be sent ASAP).
			--->
			<cfset APPLICATION.Settings.NextMessage = Now() />
 
			<!---
				This will be the an array of the last three IDs
				used. In our hectic, ColdFusion loving days,
				how can we be expected to remember which text
				messages have been sent.
			--->
			<cfset APPLICATION.Settings.PrevMessages = ArrayNew( 1 ) />
 
 
			<!--- <br>
				Check to see if our app settings file exists.
				If it does, then we are going to want to pickup
				where we left off.
			--->
			<cfif FileExists( APPLICATION.Settings.FilePath )>
 
				<!--- Read in the XML data file. --->
				<cffile
					action="READ"
					file="#APPLICATION.Settings.FilePath#"
					variable="LOCAL.SettingsData"
					/>
 
 
				<!---
					Convert the XML data into the Application
					settings structure. Do not store this
					directly into the application as we don't
					want to lose our file path. Plus, you never
					know if the structure has changed for what
					ever reason.
				--->
				<cfwddx
					action="WDDX2CFML"
					input="#LOCAL.SettingsData#"
					output="LOCAL.Settings"
					/>
 
 
				<!---
					Check to see if we have the next message
					data point and that it is a valid date.
				--->
				<cfif (
					StructKeyExists( LOCAL.Settings, "NextMessage" ) AND
					IsNumericDate( LOCAL.Settings.NextMessage )
					)>
 
					<!--- Store the setting value. --->
					<cfset APPLICATION.Settings.NextMessage = LOCAL.Settings.NextMessage />
 
				</cfif>
 
 
				<!---
					Check to see if we have the previous
					messages data point and that it is a valid
					data array..
				--->
				<cfif (
					StructKeyExists( LOCAL.Settings, "PrevMessages" ) AND
					IsArray( LOCAL.Settings.PrevMessages )
					)>
 
					<!--- Store the setting value. --->
					<cfset APPLICATION.Settings.PrevMessages = LOCAL.Settings.PrevMessages />
 
				</cfif>
 
			</cfif>
 
		</cflock>
 
 
		<!--- Return out. --->
		<cfreturn true />
	</cffunction>
 
 
	<cffunction
		name="OnRequestStart"
		access="public"
		returntype="boolean"
		output="false"
		hint="Fires prior to page processing.">
 
		<!--- Define arguments. --->
		<cfargument
			name="TargetPage"
			type="string"
			required="true"
			/>
 
 
		<!---
			Check to see if we are manually resetting the
			application. We will know to do this if the
			query param "reset" exists in the URL.
		--->
		<cfif StructKeyExists( URL, "reset" )>
 
			<!---
				Manually call the OnApplicationStart()
				event method. Let the App method take care
				of locking. We will not care at this point.
			--->
			<cfset THIS.OnApplicationStart() />
 
		</cfif>
 
		<!--- Return out. --->
		<cfreturn true />
	</cffunction>
 
 
	<cffunction
		name="OnRequest"
		access="public"
		returntype="void"
		output="true"
		hint="Fires after pre-page processing is complete. Defines which template will actually be run for the request.">
 
		<!--- Define arguments. --->
		<cfargument
			name="TargetPage"
			type="string"
			required="true"
			/>
 
		<!--- Include the requested page. --->
		<cfinclude template="#ARGUMENTS.TargetPage#" />
 
		<!--- Return out. --->
		<cfreturn />
	</cffunction>
 
</cfcomponent>

For Cut-and-Paste