<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>