#DEFINE eol CHR(13) + CHR(10)
***************************************************************************************
* The following class is the "MotherShip" class that might be used in a web application.
* All of my application specific code has been removed. What remains is the
* creation of the session manager, and the addition and destruction of a session node
* for the current thread.
*
* PLEASE NOTE THAT THIS MODULE HAS NOT BEEN TESTED SINCE REMOVING APPLICATION SPECIFIC
* PROPERTIES AND CODE.
***************************************************************************************
DEFINE CLASS MyMotherShip AS CUSTOM OLEPUBLIC
cVersion = 2
PROCEDURE Destroy
oSessionMgr.DeleteSession()
ENDPROC
PROCEDURE InitSession
PARAMETERS cPath
* If the session object is not available, create it now.
IF !IsObject("oSessionMgr")
PUBLIC oSessionMgr
oSessionMgr = CREATEOBJECT("SessionMgr")
ENDIF
oSessionMgr.AddSession(cPath)
* Now that we have a session object, initialize the remaining web components...
ENDPROC
FUNCTION ShowSession
RETURN oSessionMgr.ShowSession()
ENDFUNC
ENDDEFINE
***************************************************************************************
* The SessionMgr class is used to manage session objects for each thread in a multi-
* threaded OLE Server. A list of active sessions are maintained as a linked list.
*
* This class was implemented because the session object needs to be available throughout
* the components of the n-tier architecture. So the session object was originally
* instantiated to a PUBLIC variable. However, in a multi-threaded server, this public
* variable is seen across thread boundaries. One thread would overwrite the session of
* another. The Session Manager will be referenced through a global variable, but will
* accept and return properties of the session belonging to the active thread.
***************************************************************************************
DEFINE CLASS SessionMgr AS Custom OLEPUBLIC
oStart = .NULL.
oEnd = .NULL.
ADD OBJECT rmC AS rmContact NOINIT
PROCEDURE AddSession
PARAMETERS cPath
LOCAL oSessionPtr
* Create a new session object.
oSessionPtr = CREATEOBJECT("SessionPtr")
oSessionPtr.oSession = CREATEOBJECT("MySession", cPath)
oSessionPtr.nThreadID = Application.ThreadID
* Add the session to the list.
IF ISNULL(This.oEnd)
This.oStart = oSessionPtr
This.oEnd = oSessionPtr
ELSE
oSessionPtr.oNext = This.oEnd
This.oEnd = oSessionPtr
ENDIF
ENDPROC
PROCEDURE DeleteSession
LOCAL oSessionPtr
* Remove a session from the list.
oSessionPtr = This.FindSession()
IF ISNULL(oSessionPtr)
RETURN
ENDIF
IF ISNULL(oSessionPtr.oPrev)
This.oStart = oSessionPtr.oNext
ELSE
oSessionPtr.oPrev.oNext = oSessionPtr.oNext
ENDIF
IF ISNULL(oSessionPtr.oNext)
This.oEnd = oSessionPtr.oPrev
ELSE
oSessionPtr.oNext.oPrev = oSessionPtr.oPrev
ENDIF
* Release the session object.
oSessionPtr = .NULL.
ENDPROC
PROCEDURE Destroy
LOCAL oSessionPtr
* Release all sessions on the list.
IF ISNULL(This.oStart)
RETURN
ENDIF
DO WHILE !ISNULL(This.oStart.oNext)
oSessionPtr = This.oStart
oSessionPtr.oSession = .NULL.
This.oStart = oSessionPtr.oNext
oSessionPtr = .NULL.
ENDDO
ENDPROC
FUNCTION FindSession
LOCAL oSessionPtr
IF ISNULL(This.oStart)
RETURN .NULL.
ENDIF
oSessionPtr = This.oStart
DO WHILE !ISNULL(oSessionPtr.oNext)
IF oSessionPtr.nThreadID = Application.ThreadId
EXIT
ENDIF
oSessionPtr = oSessionPtr.oNext
ENDDO
RETURN oSessionPtr
ENDFUNC
PROCEDURE GetSessionProperty
PARAMETERS cPropertyName
LOCAL oSessionPtr, cPropertyRef, varPropertyValue
oSessionPtr = This.FindSession()
IF ISNULL(oSessionPtr)
RETURN .F.
ENDIF
cPropertyRef = "oSessionPtr.oSession." + cPropertyName
varPropertyValue = EVALUATE(cPropertyRef)
RETURN varPropertyValue
ENDPROC
PROCEDURE Init
PARAMETERS cPath
IF !EMPTY(cPath)
This.AddSession(cPath)
ENDIF
ENDPROC
PROCEDURE SetSessionProperty
PARAMETERS cPropertyName, varPropertyValue
LOCAL oSessionPtr, cPropertyRef
oSessionPtr = This.FindSession()
IF ISNULL(oSessionPtr)
RETURN
ENDIF
cPropertyRef = "oSessionPtr.oSession." + cPropertyName
STORE varPropertyValue TO (cPropertyRef)
ENDPROC
FUNCTION ShowSession
LOCAL oSessionPtr
oSessionPtr = This.FindSession()
IF ISNULL(oSessionPtr)
RETURN ""
ENDIF
RETURN oSessionPtr.oSession.ShowProperties()
ENDFUNC
ENDDEFINE
DEFINE CLASS SessionPtr AS Custom
* This class is a node in the linked list managed by the Session Manager.
nThreadID = 0
oSession = .NULL.
oNext = .NULL.
oPrev = .NULL.
ENDDEFINE
***************************************************************************************************
* The mySession class sets up a session object for the application. The purpose is to have a
* single place where all application options and environment settings can be made available to
* all components of the architecture. Since the session properties are loaded from an INI file,
* this scenario also avoids having to reload the INI file multiple times throughout the application.
***************************************************************************************************
***************************************************************************************************
* This class can be replaced completely with something that is more appropriate for your OLE server
* application. I've already ripped out most things that are specific to my application. But I've
* left this code here as an example of what the session object might look like.
*
* PLEASE NOTE THAT THIS MODULE HAS NOT BEEN TESTED SINCE REMOVING APPLICATION SPECIFIC PROPERTIES AND CODE.
***************************************************************************************************
DEFINE CLASS mySession AS CUSTOM OLEPUBLIC
cDataPath = "Data"
cErrorLog = JUSTSTEM(Application.ServerName) + [Error.LOG] && Where to write out error messages.
cINIFile = JUSTSTEM(Application.ServerName) + [.INI] && The default INI file to use.
cPath = SET("PATH") && The path setting to be used by the server.
cSavePath = SET("PATH") && The current path to be restored when exiting.
cSaveStartDir = Application.DefaultFilePath && The default directory for the server.
cServerName = Application.ServerName && The server application's name.
cStartPath = Application.DefaultFilePath && The target directory where the server should run.
cTmpPath = "Temp" && Directory where temporary files should be written.
lDebug = .F. && Whether or not debugging messages should be logged.
PROCEDURE Destroy
SET DEFAULT TO (This.cSaveStartDir)
SET PATH TO (This.cSavePath)
ENDPROC
PROCEDURE Error
LPARAMETERS nError, cMethod, nLine, cMessage
LOCAL cErrorLog
cMethod = This.Name + [.] + cMethod
This.LogError(nError, cMethod, nLine, cMessage)
ENDPROC
FUNCTION FindINI
LOCAL cMessage
* Attempt to locate an INI file based on the server name.
This.cINIFile = JUSTSTEM(This.cServerName) + [.INI]
* Use myApplication.INI as a fallback.
IF !FILE(This.cINIFile)
This.cINIFile = [myApplication.INI]
ENDIF
* If no INI is found, then the client application will have to
* depend on the default properties.
IF !FILE(This.cINIFile)
cMessage = [No Initialization file was found.]
This.LogError(-1, PROGRAM(), LINENO(), cMessage)
This.SetDefaults()
RETURN .F.
ENDIF
* Save the INI File path.
This.cINIFile = FULLPATH(This.cINIFile)
RETURN .T.
ENDFUNC
PROCEDURE Init
PARAMETERS cStartPath AS String, lDebug AS Boolean
LOCAL cPath, cServerName, cServerPath, orm, ors, oreg
* Capture the thread ID for multi-thread processing.
This.nThreadID = Application.ThreadID
IF !EMPTY(lDebug)
This.lDebug = lDebug
cMessage = [Debugging messages turned on from parameters.]
This.LogError(0, PROGRAM(), LINENO(), cMessage)
ENDIF
* If a starting path was submitted, set the default directory to it.
IF !EMPTY(cStartPath)
SET DEFAULT TO (cStartPath)
ENDIF
This.cStartPath = SYS(5) + SYS(2003)
* Add the location of the server to the path.
cServerPath = JUSTPATH(This.cServerName)
IF NOT UPPER(cServerPath) $ This.cPath
IF EMPTY(This.cPath)
This.cPath = cServerPath
ELSE
This.cPath = This.cPath + [;] + cServerPath
ENDIF
ENDIF
cPath = This.cPath
SET PATH TO &cPath.
* Process through the INI file.
This.ProcessINI()
ENDPROC
PROCEDURE LogError
PARAMETERS nError, cMethod, nLine, cMessage
LOCAL cErrorLog, cServerName, cLogString, cSessionProperty
* Logs errors to the error log.
* Log only if a real error unless the debug flag is on.
IF nError = 0 AND !This.lDebug
RETURN
ENDIF
IF EMPTY(cMessage)
cMessage = MESSAGE()
ENDIF
* Get the error log file name. The session object may not be initialized yet.
* Write the error log to the temp path.
cErrorLog = ADDBS(This.cTmpPath) + ALLTRIM(This.cErrorLog)
cLogString = eol
cLogString = cLogString + [Time: ] + TTOC(DATETIME())
cLogString = cLogString + [ Error: ] + PADL(ALLTRIM(STR(nError)), 5)
cLogString = cLogString + [ Procedure: ] + PADR(cMethod, 50)
cLogString = cLogString + [ Line: ] + STR(nLine, 4)
cLogString = cLogString + [ Message: ] + cMessage
STRTOFILE( cLogString, cErrorLog, 1)
ENDPROC
HIDDEN PROCEDURE ProcessINI
LOCAL cMessage, nFileHandle
* Reads the contents of the INI file to set properties.
* Locate the INI file.
IF !This.FindINI()
RETURN
ENDIF
cMessage = [Initialization file used: ] + This.cINIFile
This.LogError(0, PROGRAM(), LINENO(), cMessage)
* Look for initialization properties in the INI file...
STORE FOPEN(This.cINIFile) TO nFileHandle && Open the file
IF nFileHandle > 0
STORE FSEEK(nFileHandle, 0, 2) TO nEnd && Move pointer to EOF
STORE FSEEK(nFileHandle, 0) TO nTop && Move pointer to BOF
DO WHILE !FEOF(nFileHandle)
cString = ALLTRIM(FGETS(nFileHandle, nEnd)) && Store contents
DO CASE
CASE cString = "*"
&& Ignore the comment line.
CASE [DATAPATH] $ UPPER(cString)
This.cDataPath = ADDBS(ALLTRIM(SUBSTR(cString, AT("=", cString)+1)))
cMessage = [Data Path from ] + This.cINIFile + [ is ] + UPPER(This.cDataPath)
This.LogError(0, PROGRAM(), LINENO(), cMessage)
CASE [DEBUG] $ UPPER(cString)
* Send debugging messages to the error log.
IF "OFF" $ UPPER(cString)
This.lDebug = .F.
ELSE
This.lDebug = .T.
ENDIF
CASE [STARTPATH] $ UPPER(cString)
cPath = ADDBS(ALLTRIM(SUBSTR(cString, AT("=", cString)+1)))
SET DEFAULT TO (cPath)
This.cStartPath = cPath
cMessage = [Default Path from ] + This.cINIFile + [ is ] + UPPER(This.cStartPath)
This.LogError(0, PROGRAM(), LINENO(), cMessage)
CASE [TEMPPATH] $ UPPER(cString)
This.cTmpPath = ADDBS(ALLTRIM(SUBSTR(cString, AT("=", cString)+1)))
cMessage = [Temp Path from ] + This.cINIFile + [ is ] + UPPER(This.cTmpPath)
This.LogError(0, PROGRAM(), LINENO(), cMessage)
ENDDO
= FCLOSE(nFileHandle) && Close the .INI file
ELSE
This.LogError(0, PROGRAM(), LINENO(), This.cINIFile + [ file could not be opened.])
ENDIF
ENDPROC
HIDDEN PROCEDURE SetDefaults
* If properties are not provided, use these defaults.
This.cDataPath = Application.DefaultFilePath + [\Data\]
This.cTmpPath = Application.DefaultFilePath + [\Temp\]
ENDPROC
PROCEDURE ShowProperties
LOCAL cProperties
* Returns a string with the session properties.
cProperties = ;
[Thread ID: ] + ALLTRIM(STR(This.nThreadID)) + eol + ;
[Data Path: ] + This.cDataPath + eol + ;
[Error Log: ] + This.cErrorLog + eol + ;
[INI File: ] + This.cINIFile + eol + ;
[Current Path: ] + This.cPath + eol + ;
[Server Name: ] + This.cServerName + eol + ;
[Start Path: ] + This.cStartPath + eol + ;
[Saved Path: ] + This.cSavePath + eol + ;
[Saved Default Directory: ] + This.cSaveStartDir + eol + ;
[Temp Path: ] + This.cTmpPath + eol + ;
[Debug: ] + IIF(This.lDebug, "True", "False") + eol
RETURN cProperties
ENDPROC
ENDDEFINE
*!* ***************************************************************************************************
*!* * The following is sample contents for an INI file. Remove commenting before trying to use.
*!* *
*!* * Hmmm... Not myuch left after removing application specific settings!
*!* ***************************************************************************************************
*!* DEBUG OFF
*!* *[Paths]
*!* DATAPATH = Data
*!* TEMPPATH = Temp
*!* STARTPATH = C:\inetpub\[URL unfurl="true"]wwwroot\myWebApplication\[/URL]
*!* *[User Properties]