Write Secured Code Using Defensive Coding
Today's tip is a guest tip from Alain Romedenne (aromedenne at amadeus dot net). Alain is heavily involved with the OpenDOM and NSFPeek projects at OpenNTF.org, and wanted to share some of his wisdom with the Breaking Par readers. Thanks, Alain! The tip is entitled "Write Secured Code Using Defensive Coding"
Although object-oriented design programming has become unavoidable nowadays, the truth still is numerous Notes & Domino applications have been written and keep being developed using traditional procedural coding.
This week's BreakingPar tip shows how global variables usage in forms, views and script libraries can benefit from object-oriented modelling techniques building up more reliable programs.
One generally create global variables in script libraries in order to share or reuse information and processes across forms, views, agents or script libraries. Consider the following global variables in "my Library" :
Dim directory As NotesDatabase
Dim user As NotesName
Dim location As NotesDocument
All three variables are initialized whenever deemed pertinent and respectively reference my company corporate Name & Address book, foreground current user if any and the application current possible context such as "Office", "Travel", "Disconnected" and so on. While Domino applications cost-effectively reuse "my Library" three variables in forms, views and agents over and over, these globals' expose themselves excessively. Any developer, any time, anywhere can clear them by accident without noticing writing for example :
Delete directory
OR
Set user = Nothing
OR
location.Remove True
The bad thing is that one generally detects this at run-time, if not late on production. The three statements end up throwing "Object variable not set" exceptions by the time one references directory, user or location variables. In "Writing Secure Code" book at Microsoft Press Michael Howard and David LeBlanc make this point: " Never assume that your application will be run in only a few given environments. Chances are good it will be used in some other, as yet undefined, setting. Assume instead that your code will run in the most hostile of environments, and design, write, and test your code accordingly ". As tough as this recommendation may sound good news is LotusScript can help you achieve this.
A sure way to prevent from the above event is to protect global variables making them read-only, unfortunately such statement does not exist as we all know. However a solution to that problem is surprisingly offered by Domino since LotusScript language first inception in Notes release 4, although it has rarely if ever been used in available public LotusScript code. Indeed, a well kept LotusScript secret is that properties can be implemented in script libraries just as functions and subroutines do. You do not need to wrap properties within classes to use them as the following code samples demonstrate:
Property Get directory As NotesDatabase
Set directory = New NotesDatabase( <server>, <filepath> )
End Property ' directory
OR
Private m_user As NotesName
Public Property Get user As NotesName
Dim session As New NotesSession
Set m_user = New NotesName( session.UserName )
Set user = m_user
End Property ' user
When clearing by accident global variables defined as properties in script libraries, designers get warned at compile-time. Thus Delete directory statement is caught by the compiler as "<Agent>: <ProcName>: 10: DELETE not valid on: DIRECTORY" whereas Set user = Nothing statement throws an "<Library>: <ProcName>: 8: PROPERTY SET not defined for: USER" compiler error message, unless you define its setter counterpart like in:
Public Property Set user As NotesName
Set m_user = user
End Property
The above technique will suit most situations but <object>.remove situation. Applications specific cases still require to apply defensive coding in order to prevent objects from being de-referenced using either Delete or Set statements, either <object>.Remove methods.
When resorting to properties in script libraries observe complex application logic can be implemented:
- imagine "directory" property encompassing computation based upon cascaded address books and/or condensed or partial directories
- say "location" property initializes itself every time it detects it requires to do so although the location.remove indirect statement remains undetected but at run-time:
Private m_location As NotesDocument
Public Property Get location As NotesDocument
If m_location Is Nothing Then ' creates & loads an in-memory copy of current location
Set m_location = directory.createDocument
Call directory.GetView( "Locations" ).getDocumentByKey( <Value> ).CopyAllItems( m_location )
End If
Set location = m_location
End Property ' location
Don't wait any longer and start right now making sure your procedural LotusScript code benefits from object-oriented programming simple techniques !