dimanche 9 décembre 2007

Notes/Domino DOM LotusScript Mis à Nu


Voici une courte liste des méthodes et propriétés LotusScript non documentées en version 8, construite à l'aide de la librairie notes32.tlb de Lotus Notes.

Quelques précautions d'usage s'appliquent puisqu'un tel contenu est non supporté par IBM. Utilisez cela librement mais sans garantie ni support.
  • NotesDatabase
    archiveDestinations : Variant
  • NotesDocument
    SequenceTime : Variant

Plot Undocumented DOM LotusScript Using Visual Basic 2005 Express free Edition et Use Rational Rose to Identify Undocumented DOM Properties and Methods détaillent comment identifier les fonctions cachées du LotusScript de Notes ou Domino, depuis le support de COM dans la version 5. Undocumented DOM LotusScript Inventory contient un inventaire détaillé des propriétés et méthodes non documentées en Version 7.

mercredi 25 juillet 2007

Lotus Jaune rama




vendredi 6 avril 2007

Interfaces & Classes Abstraites en LotusScript


Les classes abstraites et les interfaces définissent des propriétés et des méthodes, au même titre que les classes traditionnelles. Il est possible de créer autant d'objets que nécessaire à partir de classes standards, alors que les interfaces ne servent qu'à décrire les fonctions que couvriront de futurs objets. Généralement les interfaces ne contiennent pas de code et ne permettent pas d'instancier des objets. Les classes abstraites, quant à elles, prototypent des propriétés et des méthodes. Il n'est pas permis non plus d'instancier directement des classes abstraites, celles-ci doivent être dérivées afin que leurs sous-classes héritent de leur code. Les compilateurs contrôlent généralement la couverture de toutes les propriétés et méthodes et s'assurent que nul n'instancie des interfaces ou des classes abstraites.

D'ordinaire les concepteurs utilisent les interfaces et les classes abstraites pour construire des applications à faible couplage au code plus adaptable, plus maintenable et finalement plus robuste. Bien qu'absents du langage LotusScript, ces concepts, issus de la conception orientée objet, sont aisément implémentables en LotusScript.

Imaginez la classe LotusScript.io.File aux propriétés et méthodes suivantes: canWrite( ), getAbsolutePath( ), length( ), list( ), renameTo( ). Il est envisageable de la construire simplement en LotusScript, mais aussi avec LS2J en s'appuyant sur Java, comme d'utiliser le composant COM Windows Script Host ou bien d'appeler les API 32-bits natives de Windows. Il devient alors tentant de définir une interface iFile décrivant ces propriétés et méthodes indépendamment de la solution finale restant à valider. Vous fabriquez ainsi une application dépendant d'une seule interface stable, alors que l'implémentation concrète peut varier à loisir sans heurts pour le code appelant. Voilà une application Notes ou Domino faiblement couplée.

Figure 1: Le diagramme de classes UML de StopWatches

Le projet libre OpenDOM téléchargeable sur www.openntf.org fournit bon nombre d'exemples utilisant les notions de classe abstraite ou d'interface. La bibliothèque LotusScript.langStopWatches définit une interface publique iTicker à partir de laquelle quatre implémentations différentes ont été construites. Elles supportent LotusScript version 3 ou 4+ ainsi que Windows 16-bits ou 32-bits. Les paquetages, ou espaces de nommages, LotusScript.lang.Rn ou LotusScript.windows.winnn hébergent chacun une classe Ticker implémentant concrètement l'interface iTicker. La classe publique StopWatches charge dynamiquement le moteur, construit d'après iTicker, qui est le plus adapté au contexte courant.

Vous pouvez éviter l'instanciation d'objets et vous assurer que les propriétés et les méthodes sont surchargées ou dérivées. Mais créer un constructeur de classe LotusScript privé comme ..
Public Class iVehicle
Private Sub New
End Sub
End Class ' iVehicle
.. est refusé par le compilateur :
<notename>: <eventname>: <linenumber>: Illegal PRIVATE declaration of: NEW

Puisque LotusScript ne supporte pas les interfaces ou les constructeurs privés j'utilise une technique différente. Je m'assure qu'aucune instanciation d'interface ou de classe abstraite n'est réalisée à l'exécution seulement en lançant une exception au sein du constructeur de classe. Je force également la dérivation des propriétés et des méthodes par d'autres exceptions comme suit :
%INCLUDE "LsErr.lss"
Public Class iVehicle '
Public Sub New ' This forbids interface instantiation, please derive me !
If Typename( Me ) = "IVEHICLE" Then Error ErrNotAnObject
End Sub ' New
Public Property Get Color As NotesColor
Error errPropGetNotDefined
End Property ' Color
Public Property Set Price As Currency
Error errPropSetNotDefined
End Property ' Price
Public Sub starts( )
Error ErrNotAMethod
End Sub ' starts( )
End Class ' iVehicle

Public Class aShuttle
Public Property Get isFree As Boolean '
Error errPropSetNotDefined
End Property ' isFree
End Class ' aShuttle

Une fois mon interface décrite, je peux réaliser autant de classes concrètes qui l'implémentent et fournir une fabrique de classe ou une routine destinée à distribuer des objets différents selon le contexte :
Private Class Car As iVehicle
' Your code goes here ..
End Class ' Car As iVehicle

Private Class Truck As iVehicle
' Your code goes here ..
End Class ' Truck As iVehicle

Public Function createVehicle As iVehicle
If ( so And so ) Then
Set Me.createVehicle = New Car
Else
Set Me.createVehicle = New Truck
End If
End Function

La classe LotusScript.lang.Factory d'OpenDOM crée des objets de cette façon. Elle s'inspire d'un redbook d'IBM intitulé « Performance Considerations for Domino Applications » dont l'annexe « B-2 Dynamic Script Library Loading » décrit d'intelligente manière comment charger dynamiquement des bibliothèques LotusScript dans vos applications orientées objet. Toutefois le code fournit par IBM réalise celà à l'aide de variants, c'est à dire au dépend d'un typage fort des données contrôlable par le compilateur. L'ajout d'interface dans votre code fixe celà et a de multiples avantages :
  • Il est possible de concevoir des applications à faible couplage en LotusScript qui s'affranchissent des implémentations techniques sous-jacentes
  • Les données font l'objet d'un typage fort au moment de la compilation, pratique vivement recommandée
  • L'empreinte mémoire de l'interpréteur LotusScript augmente progressivement à mesure que les bibliothèques sont appelées. Une compilation initiale précède ces appels, mais elle n'est pas notable alors qu'elle est pleinement compensée par une utilisation mémoire minime.
Pour réaliser une reconnaissance des objets dynamiques à la compilation, la classe Factory utilise des interfaces ou des classes abstraites. A contrario de la routine NewObj d'IBM, la classe Factory instancie des objets fortement typés, le contrôle des types lors de l'écriture du code renforce la robustesse et la stabilité des applications.

Lectures annexes:

mercredi 21 mars 2007

Propriétés et Méthodes Statiques en LotusScript


En Java et JavaScript les propriétés et méthodes statiques sont partagés par toutes les instances d'une classe donnée. Les membres non statiques sont liés aux objets d'une classe, ils appartiennent à l'instance d'une classe ainsi les instances de la classe File partagent la propriété statique pathSeparator qui dépend du système d'exploitation, alors que les propriétés getAbsolutePath( ) et canWrite( ) diffèrent d'un objet à un autre et que la méthode delete( ) produit un résultat différent selon l'objet qui l'exécute. Il est inutile d'instancier des objets pour utiliser les membres statiques et la propriété pathSeparator de la classe java.io.File est référençable en codant File.pathSeparator ou bien java.io.File.pathSeparator.

Il peut être utile de créer en LotusScript des méthodes ou des propriétés de classe. Considérez une classe Platform et ses deux propriétés Newline et supportsCOM. Bien que ces propriétés varient selon le système d'exploitation, elles sont constantes pour toutes les instances de la classe Platform pour un client Notes ou serveur Domino donné. Imaginez la classe Trigo d'un paquetage LotusScript.math ses méthodes sin(), cos() et une propriété statique Pi aux précisions décimales supérieures à celles offertes nativement en LotusScript par la constante Pi et les fonctions trigonométriques.

Bien que le langage LotusScript ne dispose pas de membres statiques au niveau où Java et JavaScript les proposent, vous pouvez les implémenter de façon similaire avec une syntaxe très proche. La propriété Release de la classe LotusScript.lang.Interpreter disponible dans le projet libre OpenDOM sur le site www.openntf.org permet d'illustrer celà.

Private Release As String
Release = lsi_info( 6 )

Alors que ces deux lignes initialisent très simplement la version de l'interpréteur LotusScript, je leur préfère le codage défensif suivant destiné à protéger la variable globale Release de toute modification :

Static Private Property Get INTERPRETER_RELEASE As String
Static this As String
If this = "" Then this = Lsi_info( 6 )
INTERPRETER_RELEASE = this
End Property ' LotusScript.lang.Interpreter.RELEASE property

La version LotusScript réduite de la classe publique Interpreter s'écrit comme suit :

Public Class Interpreter '
Property Get RELEASE As String '
Me.RELEASE = INTERPRETER_RELEASE
End Property ' LotusScript.lang.Interpreter.RELEASE
End Class ' LotusScript.lang.Interpreter Class

De cette façon tous les objets instanciés de la classe Interpreter partagent une propriété RELEASE qualifiable de statique comme en Java ou JavaScript. La syntaxe Interpreter.RELEASE est implémentable dans la bibliothèque "LotusScript.lang" de la manière suivante :

Static Public Property Get Interpreter As Interpreter
Static this As Interpreter
If this Is Nothing Then Set this = New Interpreter
Set Interpreter = this
End Property ' LotusScript.lang.Interpreter class

Deux courtes classes et deux propriétés supplémentaires suffisent à mentionner le chemin pleinement qualifié d'une classe comme dans LotusScript.lang.Interpreter.RELEASE :

Public Class lang '
Public Property Get Interpreter '
Set Me.Interpreter = LANG_INTERPRETER
End Property ' LotusScript.lang.Interpreter
End Class ' LotusScript.lang.* Package
Public Class LotusScript '
Public Property Get lang As lang '
Set Me.lang = LOTUSSCRIPT_LANG
End Property ' LotusScript.lang.* Package
Public io As Variant ' As io__ '
Public net As Variant ' As net__ '
End Class ' LotusScript.* Package

Static Private Property Get LOTUSSCRIPT_LANG As lang
Static this As lang
If this Is Nothing Then Set this = New lang
Set LOTUSSCRIPT_LANG = this
End Property ' LotusScript.lang.* package
Static Public Property Get LotusScript As LotusScript
Static this As LotusScript
If this Is Nothing Then
Set this = New LotusScript
End If
Set LotusScript = this
End Property ' LotusScript.* package

Réutiliser complètement la bibliothèque "LotusScript.lang" dans un agent, un masque ou une vue se résume à :

Use "LotusScript.lang"
Msgbox LotusScript.lang.Interpreter.RELEASE,, _
"LotusScript.lang.Interpreter.RELEASE"
' OU BIEN
With LotusScript.lang
Msgbox Interpreter.RELEASE,, _
"Interpreter.RELEASE"
End With

dimanche 11 mars 2007

Eradiquer l'infâmant « Public symbol is declared in another module: V_EMPTY »


Inclure les fichiers *.lss demeure le cauchemar des programmeurs Notes. En effet, de nombreux développeurs ont abandonné la pratique de %INCLUDE "LsConst.lss" ou d'autres bibliothèques dans leur code. Nombreux son ceux qui redéfinissent des constantes privées dans leurs bibliothèques, masques ou vues, ce qui n'est pas recommandable.

Une solution aurait été de définir les constantes globales du LotusScript et de Notes au sein d'un fichier .lsx connu de "Domino Designer". Mais IBM/Lotus n'a apparemment pas envisagé celà.

Jusqu'à maintenant, ma solution consiste à créer une bibliothèque "LsConst" référençable au sein des éléments de conception. Les constantes ne doivent être déclarées ni publiques ni privées et la bibliothèque débute par Option Public. Les bibliothèques, les masques ou les vues la référencent par Use "LsConst" et incluent indifféremment Option Private | Public et/ou Option Explicit | Declare; notez cependant que Option Declare est vivement recommandé !

Les avantages sont multiples :
  • "LsConst" est référençable SIMULTANEMENT dans les bibliothèques, les masques, les vues etc.. elle est INTEGRALEMENT réutilisée.
  • Les constantes présentes au sein des bibliothèques liées SONT accessibles e.g. je peux référencer la constante LSI_THREAD_CALLPROC dans mon code avec USE "LsConst" puisque la bibliothèque "LsPrcVal" est enchâssée dans "LsConst".
  • La taille des bibliothèques de script est réduite, (code source et code objet) en référençant "LsConst" comme décrit précédemment.
  • Aucune Option de compilation n'est imposée au développeur.
  • La bibliothèque "LsConst" n'est pas nécessaire en production (sauf dans les rares cas où les bibliothèques de script sont dynamiquement chargées à l'exécution...)
Quelques inconvénients demeurent:
  • L'installation d'une nouvelle version de Notes ou Domino nécessite une relecture attentive des librairies Lsconst.lss, Lserr.lss, Lsxbeerr.lss et lsxuierr.lss.
Les bibliothèques "LotusScript.exceptions" et "LotusScript.lang.Lsconst", du project public OpenDOM, exploitent ce mécanisme et sont consultables sur www.openntf.org.

dimanche 25 février 2007

Code Sécurisé et Programmation Défensive

Alors que la programmation orientée objet est devenue inévitable de nos jours, de nombreuses applications Notes & Domino demeurent écrites ou sont développées en utilisant une programmation procédurale traditionnelle.

Ce billet illustre comment les variables globales présentes dans les masques, les vues ou les bibliothèques de script peuvent profiter des techniques de modélisation et de programmation objet afin de produire des programmes plus sûrs.

Les variables globales servent à partager et à réutiliser les données ou routines entre masques, vues ou bibliothèques de script. Examinons les 3 variables que contient "ma Bibli" :

Dim directory As NotesDatabase
Dim
user As NotesName
Dim location As NotesDocument
Ces trois variables sont initialisées dès que possible et représentent respectivement le carnet d'adresses d'entreprise, l'utilisateur courant et le contexte applicatif tel que "Bureau", "Déplacement", "Local" ainsi de suite. Les applications Domino peuvent réutiliser à faible coût les trois variables de "ma Bibli" au sein de masques, de vues ou d'agents. Cependant ces trois variables sont trop exposées, en effet tout développeur à tout moment, en tout lieu peut réinitialiser celles-ci par mégarde en codant par exemple :
Delete directory
' OU
Set user = Nothing
' OU
location.Remove True
De telles failles sont d'ordinaire détectées à l'exécution des tests mais parfois également en production. Ces trois instructions génèrent la trop fameuse exception "Object variable not set" lorsque l'on tente de référencer les variables directory, user ou bien location. Dans leur livre "Writing Secure Code", édité par Microsoft, Michael Howard et David LeBlanc mentionne ce point: « Ne supposez jamais que votre application s'exécutera dans un nombre limité de contextes. Il y a de grandes chances qu'un utilisateur l'exécutera dans un environnement encore inconnu de vous. Supposez au contraire que votre code s'exécutera dans un context des plus hostiles, et concevez, codez et testez vos programmes en conséquence ». Aussi désagréable que cette recommandation paraisse, une bonne nouvelle est que le langage LotusScript permet de réaliser cet objectif.

Une manière efficace de protéger nos variables est de les déclarer en lecture seule, malheureusement un telle instruction n'existe pas en LotusScript. Une solution à ce problème existe cependant depuis la première mouture du Langage LotusScript implémentée dans Notes version 4, bien qu'elle n'ait rarement sinon jamais été publiquement exposée sur la Toile. En effet, la documentation LotusScript dissimule le fait que les propriétés peuvent être implémentées dans les bibliothèques de script au même titre que les fonctions et les routines. Il n'est pas nécessaire de passer par une classe pour définir une propriété comme le montre le code suivant :
Property Get directory As NotesDatabase
Set directory = New NotesDatabase( <server>, <filepath> )
End Property ' directory
' OU
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
Lors d'un déréférencement accidentel de variables globales définies ainsi dans les bibliothèques de script, les développeurs sont alertés lors de la compilation. Ainsi l'instruction Delete directory est interceptée par le compilateur qui affiche "Agent: Routine: n° ligne: DELETE not valid on: DIRECTORY" alors que l'instruction Set user = Nothing génère le message "Bibliothèque: Routine: n° ligne: PROPERTY SET not defined for: USER", sauf à définir l'accesseur suivant :
Public Property Set user As NotesName
Set m_user = user
End Property ' user
Cette technique répond à toutes les situations excepté celle de <object>.Remove. Des cas particuliers existent pour chaque application et nécessitent le recours à la programmation défensive afin de protéger les objets d'un déréférencement involontaire par instruction Delete ou Set , ou bien à travers la méthode <object>.Remove.
En recourant aux propriétés dans les bibliothèques de script, il est possible d'implémenter une logique applicative complexe :
  • La propriété "directory" peut traduire une recherche au sein d'une concaténation de carnet d'adresses d'entreprise ou bien des répertoires agrégés et/ou partiels
  • La propriété "location" peut se ré-initialiser seule chaque fois que nécessaire alors que le dé-référencement par location.remove demeure indétecté à la compilation comme à l'exécution :
Public Property Get location As NotesDocument
If m_location Is Nothing Then ' instancions une copie mémoire de location
Set m_location = directory.createDocument
Call directory.GetView( "Locations" ).getDocumentByKey( <Value> ).CopyAllItems( m_location )
End If
Set location = m_location
End Property ' location
N'attendez pas plus longtemps ! Introduisez dès maintenant ces pratiques simples de la programmation orientée objet au sein de vos bibliothèques de scripts en code procédural !

mardi 21 novembre 2006

Histoire oubliée du LotusScript

ou LotusScript en Mode Console

Préambule

La version 4 de LotusScript, intégrée au sein de Lotus Notes et Domino depuis la version 5, mentionne les deux constantes LSI_THREAD_MODULE et LSI_THREAD_CALLMODULE comme arguments de la fonction GetThreadInfo(), Cependant elles ne permettent aucunement l'affichage du module courant ou du module appelant comme le stipule la documentation de Domino. Elles semblent n'avoir d'utilité ni dans Notes/Domino ni dans SmartSuite, au moins à la lumière des essais que j'ai réalisés. La seule situation ou ces constantes fonctionnent est le LotusScript en mode console. Lancer LotusScript en mode console signifie exécuter des fichiers .lss hors des environnements de Notes ou de SmartSuite. J'ai créé des interpréteurs de LotusScript - pour les plateformes Windows 16-bits et 32-bits - en ayant recours aux kits d'installation des versions précédentes de Lotus qui utilisaient initialement le LotusScript. Vous trouverez ci-après comment construire ces interpréteurs.

Le fichier .zip ci-joint contient les fichiers et instructions permettant l'installation d'un « lnterpréteur LotusScript 2.01 pour Windows/16 » qui fonctionne encore sous Windows/32. Lisez les documents A propos et Aide et constatez comme les instructions lsi_info(3) et lsi_info(13) fonctionnent parfaitement ! Ouvrez le contenu du fichier .zip avant de lire plus avant.

LotusScript.lang 2.01 for Windows16.zip (795 Kbytes)

Exécution de l'Exemple

Le document A Propos contient l'aide en ligne "en anglais" de la version 2.01 de LotusScript.

  • Le document Aide contient les binaires de l'interpréteur à extraire en C:\LSI comme suggéré.
  • Exportez la bibliothèque « Module » dans ce même répertoire sous le nom Module.lss
  • Lancez la commande « LSIRUN Module.lss » et observez le résultat

Histoire Oubliée du LotusScript


lsi_info(3) et lsi_info(13) sont documentées depuis la version 4 de LotusScript - c'est à dire Notes/Domino version 5 - en tant que GetThreadInfo(LSI_THREAD_MODULE) et GetThreadInfo(LSI_THREAD_CALLMODULE). Vous avez constaté, et tous les développeurs Domino avec vous, que ces deux valeurs produisent des résultats inutilisables, que l'on se serve de Notes/Domino ou de SmartSuite. Cependant rappelez-vous que le LotusScript fut initialement intégré dans le produit Lotus Improv maintenant disparu, alors que dans sa version 2 il accompagnait un autre produit défunt appelé Notes ViP. Je suis porté à croire que lsi_info(3) et lsi_info(13), bien que jamais documentées, servaient en mode console et devinrent inutiles par la suite au sein de la version 4 de Notes, alors qu'IBM/Lotus ne remarquait pas leur utilité disparue lors des développements des IDEs de SmartSuite et Domino Designer.

Vous suivez toujours ! Voici comment construire un interpréteur LotusScript - Aussi RIDICULE que celà soit !

Récupérez de vieux CDs d'installation des versions 4.5 ou 4.6 de Notes - je ne suis plus très sur à propos de la version 5 - et interrompez l'installation de Domino après la décompaction des premiers binaires et avant de répondre aux options de configuration. Vous avez maintenant, présents sur votre disque, les fichiers temporaires constituant le moteur d'un interpréteur de LotusScript avec lequel jouer. Copiez le répertoire temporaire d'installation en C:\LSI, annulez l'installation et vous voilà prêt à écrire des sources .lss que vous exécuterez avec LSIRUN.EXE.

PS: Notez comme le sujet de la programmation orientée objet (POO) était très proprement documenté dans l'aide en ligne pour cette version 2 de LotusScript, alors qu'il disparut de l'aide de Domino: Une autre omission malheureuse d'IBM/Lotus lors de l'inclusion du LotusScript dans Domino version 4.

Locations of visitors to this page