MODULE PrevalenceSystem; (** AUTHOR "Luc Blaeser"; PURPOSE "Prevalence System - Persistent Object System"*) IMPORT XML, XMLObjects, XMLScanner, XMLParser, TFClasses, Strings, Modules, Kernel, Files, Streams, KernelLog, Configuration; CONST DEBUG = FALSE; (* debug output *) ConfigurationSupperSectionName = "PrevalenceSystem"; ConfigurationSubSectionName = "PersistentObjectModules"; ProcNameGetDescriptors = "GetPersistentObjectDescriptors"; SnapShotIntervall = 15 * 60 * 100; (* 15 minutes *) StandardPrevSystemName = "StandardPrevalenceSystem"; StandardSnapShotFileName = "PrevalenceSnapShot.XML"; StandardLogFileName = "PrevalenceLog.XML"; XMLProlog = ''; XMLRootElemName = "instances"; XMLOidCounterAttrName = "oidcounter"; XMLInstanceElemName = "instance"; XMLAttrModuleName = "module"; XMLAttrObjectName = "object"; XMLAttrOidName = "oid"; XMLAttrIsRootName = "isroot"; XMLAttrSavingCounter ="time"; XMLLogRootElemName = "log"; XMLLogDelInstElemName = "deleted"; (* only for root elements *) TermTimeout = 1000*1000; (* Wait time to terminate *) (** abstract base type for all persistent objects. * Since there is no introspection of object fields possible the user has to implement * the externalization and internalization of an object state in XML on its own. * Each command on the PersistentObject which modifies its state must be explicitly done in a * BeginTransaction..EndTransaction block to ensure logging of the state changement. * All persistent objects have to be registered by the prevalence system to make sure that their * persistent object descriptor is known. * Certain persistent objects are registered as root objects to contribute to the root set * of reachable persistent objects for the persistent object garbage collector of the prevalence system. * Each persistent object can belong only to one prevalence system *) TYPE PersistentObject* = OBJECT VAR oid*: LONGINT; (** oid of the persistent object *) inModification, takingSnapShot: BOOLEAN; registeredAt*: PrevalenceSystem; (* prevalence system where this object is registered *) PROCEDURE &Init*; BEGIN oid := 0; inModification := FALSE; takingSnapShot := FALSE; registeredAt := NIL END Init; PROCEDURE BeginModification*; BEGIN {EXCLUSIVE} AWAIT((~takingSnapShot) & (~inModification)); inModification := TRUE; END BeginModification; PROCEDURE EndModification*; (** after the return of this method the transaction is commited *) BEGIN {EXCLUSIVE} IF (registeredAt # NIL) THEN registeredAt.Log(SELF) ELSE HALT(9999); (* Object must be registered in at least one prevalence system *) END; inModification := FALSE END EndModification; PROCEDURE Externalize*() : XML.Content; BEGIN HALT(309); RETURN NIL; END Externalize; PROCEDURE Internalize*(xml: XML.Content); BEGIN HALT(309) END Internalize; PROCEDURE GetReferrencedObjects*() : PersistentObjectList; (** return all persistent objects which are referrenced by instance variables *) BEGIN RETURN NIL END GetReferrencedObjects; END PersistentObject; PersistentObjectList* = POINTER TO ARRAY OF PersistentObject; PersistentObjectFactory* = PROCEDURE (): PersistentObject; (** used to instantiate a persistent object since there is no introspection possible *) PersistentObjectDescriptor* = OBJECT VAR moduleName*, objectName*: Strings.String; factory*: PersistentObjectFactory; PROCEDURE &Init*(CONST modName, objName: ARRAY OF CHAR; factoryProc: PersistentObjectFactory); BEGIN NEW(moduleName, LEN(modName)); NEW(objectName, LEN(objName)); COPY(modName, moduleName^); COPY(objName, objectName^); factory := factoryProc END Init; END PersistentObjectDescriptor; PersistentObjectDescSet* = OBJECT VAR descriptors*: POINTER TO ARRAY OF PersistentObjectDescriptor; PROCEDURE &Init*(CONST descs: ARRAY OF PersistentObjectDescriptor); VAR i: LONGINT; BEGIN NEW(descriptors, LEN(descs)); FOR i := 0 TO LEN(descs)-1 DO descriptors[i] := descs[i] END END Init; PROCEDURE GetCount*() : LONGINT; BEGIN RETURN LEN(descriptors^) END GetCount; PROCEDURE GetItem*(i: LONGINT) : PersistentObjectDescriptor; BEGIN RETURN descriptors[i] END GetItem; END PersistentObjectDescSet; PersistentObjectDescSetFactory = PROCEDURE() : PersistentObjectDescSet; (** additionally there must be a procedure which gives all descriptors for the persistent objects in the module PROCEDURE GetPersistentObjectDescriptors*(par:ANY) : ANY; no parameter; returns the descriptors of active elements (PersistentObjectDescSet) must be thread safe *) (** returns true iff the persistent object satisfies the predicate *) FilterPredicate* = PROCEDURE {DELEGATE} (obj: PersistentObject) : BOOLEAN; (* belongs to exactly one prevalence system *) PersistentObjectWrapper = OBJECT VAR prevalenceSystem: PrevalenceSystem; instance: PersistentObject; descriptor: PersistentObjectDescriptor; savingCounter: LONGINT; (* when was the object the last time saved in the snapshot *) isRoot: BOOLEAN; (* true iff the object belongs to the root set of the mark phase *) isMarked: BOOLEAN; (* temporary use in the mark phase, true iff not garbage *) PROCEDURE &Init*(prevSys: PrevalenceSystem; obj: PersistentObject; desc: PersistentObjectDescriptor); BEGIN ASSERT(prevSys # NIL); ASSERT(obj # NIL); ASSERT(desc # NIL); prevalenceSystem := prevSys; IF (obj.oid = 0) THEN obj.oid := prevalenceSystem.GetNewOid() END; (* set an oid if not done yet *) instance := obj; descriptor := desc; isMarked := TRUE (* don't remove in a now running GC sweep phase *) END Init; END PersistentObjectWrapper; SnapShotManager = OBJECT VAR timer: Kernel.Timer; alive, terminated: BOOLEAN; i: LONGINT; p: ANY; prevSys: PrevalenceSystem; BEGIN {ACTIVE} IF (DEBUG) THEN KernelLog.String("Prevalence System: Snapshot Manager started."); KernelLog.Ln END; NEW(timer); alive := TRUE; terminated := FALSE; timer.Sleep(SnapShotIntervall); WHILE (alive) DO prevSystemList.Lock; FOR i := 0 TO prevSystemList.GetCount()-1 DO p := prevSystemList.GetItem(i); prevSys := p(PrevalenceSystem); IF (DEBUG) THEN KernelLog.String("Prevalence System '"); KernelLog.String(prevSys.SystemName^); KernelLog.String("': Storing a snapshot."); KernelLog.Ln END; prevSys.PersistAllObjects; IF (DEBUG) THEN KernelLog.String("Prevalence System '"); KernelLog.String(prevSys.SystemName^); KernelLog.String("': Snapshot done."); KernelLog.Ln END END; prevSystemList.Unlock; timer.Sleep(SnapShotIntervall) END; IF (DEBUG) THEN KernelLog.String("Prevalence System: Snapshot Manager terminated."); KernelLog.Ln END; terminated := TRUE; END SnapShotManager; TYPE PrevalenceSystem* = OBJECT VAR SnapShotFileName*: Strings.String; LogFileName*: Strings.String; SystemName*: Strings.String; persistentObjectList: TFClasses.List; (* List of PersistentObjectWrapper *) oidCounter: LONGINT; (* file access synchronization *) lockSnapShotFile: BOOLEAN; lockLogFile: BOOLEAN; (* persistent object list synchronization *) lockPersList: BOOLEAN; (** the prevalence system name, the snapshot file name and the log file name must be different to * those of the other present prevalence systems. *) PROCEDURE &Init*(CONST name, snapShotFn, logFn: ARRAY OF CHAR); VAR i: LONGINT; p: ANY; prevSys: PrevalenceSystem; BEGIN LockPrevSystemList; prevSystemList.Lock; FOR i := 0 TO prevSystemList.GetCount()-1 DO p := prevSystemList.GetItem(i); prevSys := p(PrevalenceSystem); IF ((prevSys.SystemName^ = name) OR (prevSys.SnapShotFileName^ = snapShotFn) OR (prevSys.LogFileName^ = logFn)) THEN prevSystemList.Unlock; UnlockPrevSystemList; HALT(9999) (* conflict with other prevalence system *) END END; prevSystemList.Unlock; NEW(SystemName, LEN(name)+1); COPY(name, SystemName^); NEW(SnapShotFileName, LEN(snapShotFn)+1); COPY(snapShotFn, SnapShotFileName^); NEW(LogFileName, LEN(logFn)+1); COPY(logFn, LogFileName^); NEW(persistentObjectList); oidCounter := 1; lockSnapShotFile := FALSE; lockLogFile := FALSE; lockPersList := FALSE; RestoreAllObjects; prevSystemList.Add(SELF); UnlockPrevSystemList; END Init; (** each persistent object has to be registered in the prevalence system to make sure that its descriptor is known. * This does not affect that the object will be collected as garbage if it is not reachable through a root persistent object *) PROCEDURE AddPersistentObject*(obj: PersistentObject; desc: PersistentObjectDescriptor); VAR wrapper : PersistentObjectWrapper; BEGIN LockPersistentObjList; IF ((desc # NIL) & (FindRegisteredDescriptor(desc.moduleName^, desc.objectName^) # NIL)) THEN IF ((obj # NIL) & (GetRegisteredWrapper(obj) = NIL)) THEN (* object is not registered yet *) IF (obj.registeredAt = NIL) THEN obj.registeredAt := SELF ELSIF (obj.registeredAt # SELF) THEN UnlockPersistentObjList; KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': Cannot add objects which are alreaduy registered in another prevalence system."); KernelLog.Ln; HALT(9999) END; NEW(wrapper, SELF, obj, desc); persistentObjectList.Add(wrapper); UnlockPersistentObjList; Log(obj) ELSE UnlockPersistentObjList END ELSE UnlockPersistentObjList; KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': Cannot add objects with an unregistered descriptor to the prevelance system"); KernelLog.Ln; HALT(9999) END END AddPersistentObject; (** add object to the root set of the prevalence system. This objects must be manually removed from the prevalence system, * All objects reached by a root persistent object are also persistent *) PROCEDURE AddPersistentObjectToRootSet*(obj: PersistentObject; desc: PersistentObjectDescriptor); VAR wrapper : PersistentObjectWrapper; BEGIN LockPersistentObjList; IF ((desc # NIL) & (FindRegisteredDescriptor(desc.moduleName^, desc.objectName^) # NIL)) THEN IF (obj # NIL) THEN wrapper := GetRegisteredWrapper(obj); IF (wrapper = NIL) THEN (* object is not registered yet *) IF (obj.registeredAt = NIL) THEN obj.registeredAt := SELF ELSIF (obj.registeredAt # SELF) THEN UnlockPersistentObjList; KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': Cannot add objects which are alreaduy registered in another prevalence system."); KernelLog.Ln; HALT(9999) END; NEW(wrapper, SELF, obj, desc); wrapper.isRoot := TRUE; persistentObjectList.Add(wrapper) ELSE wrapper.isRoot := TRUE END; UnlockPersistentObjList; Log(obj) END ELSE UnlockPersistentObjList; KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': Cannot add objects with an unregistered descriptor to the prevelance system"); KernelLog.Ln; HALT(9999) END END AddPersistentObjectToRootSet; (** the object will be marked to be no more belonging to the root set and all persistent objects only reachable by this * object will be removed in the next garbage collection phase *) PROCEDURE RemovePersistentRootObject*(obj: PersistentObject); VAR wrapper: PersistentObjectWrapper; BEGIN LockPersistentObjList; wrapper := GetRegisteredWrapper(obj); IF ((wrapper # NIL) & (wrapper.isRoot)) THEN wrapper.isRoot := FALSE; UnlockPersistentObjList; LogRemovalFromRootSet(wrapper) ELSE UnlockPersistentObjList END END RemovePersistentRootObject; PROCEDURE GetPersistentObject*(oid: LONGINT): PersistentObject; VAR wrapper: PersistentObjectWrapper; BEGIN wrapper := GetRegisteredWrapperByOid(oid); IF (wrapper # NIL) THEN RETURN wrapper.instance ELSE RETURN NIL END END GetPersistentObject; PROCEDURE GetDescriptorByObject*(obj: PersistentObject) : PersistentObjectDescriptor; VAR wrapper: PersistentObjectWrapper; BEGIN wrapper := GetRegisteredWrapper(obj); IF (wrapper # NIL) THEN RETURN wrapper.descriptor END END GetDescriptorByObject; PROCEDURE FindPersistentObjects*(pred: FilterPredicate) : PersistentObjectList; VAR i: LONGINT; p: ANY; wrapper: PersistentObjectWrapper; obj: PersistentObject; list: TFClasses.List; persList: PersistentObjectList; BEGIN NEW(list); persistentObjectList.Lock; FOR i := 0 TO persistentObjectList.GetCount()-1 DO p := persistentObjectList.GetItem(i); wrapper := p(PersistentObjectWrapper); (* wrapper # NIL *) obj := wrapper.instance; IF (pred(obj)) THEN list.Add(obj) END END; persistentObjectList.Unlock; IF (list.GetCount() > 0) THEN NEW(persList, list.GetCount()); FOR i := 0 TO list.GetCount()-1 DO p := list.GetItem(i); obj := p(PersistentObject); persList[i] := obj END; RETURN persList ELSE RETURN NIL END END FindPersistentObjects; PROCEDURE GetNewOid() : LONGINT; BEGIN {EXCLUSIVE} INC(oidCounter); RETURN oidCounter-1 END GetNewOid; PROCEDURE GetXMLDocument(file: Files.File) : XML.Document; VAR scanner: XMLScanner.Scanner; parser: XMLParser.Parser; doc: XML.Document; reader: Files.Reader; BEGIN (* file # NIL *) NEW(reader, file, 0); NEW(scanner, reader); NEW(parser, scanner); LockParsingScanning; scanner.reportError := ReportXMLParserScannerError; parser.reportError := ReportXMLParserScannerError; doc := parser.Parse(); UnlockParsingScanning; IF (xmlParserErrorOccurred) THEN KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': "); KernelLog.String(xmlParserErrorMsg); KernelLog.Ln; RETURN NIL ELSE RETURN doc END END GetXMLDocument; PROCEDURE LockSnapShotFile; BEGIN {EXCLUSIVE} AWAIT(~lockSnapShotFile); lockSnapShotFile := TRUE END LockSnapShotFile; PROCEDURE UnlockSnapShotFile; BEGIN {EXCLUSIVE} lockSnapShotFile := FALSE END UnlockSnapShotFile; PROCEDURE LockLoggingFile; BEGIN {EXCLUSIVE} AWAIT(~lockLogFile); lockLogFile := TRUE END LockLoggingFile; PROCEDURE UnlockLoggingFile; BEGIN {EXCLUSIVE} lockLogFile := FALSE END UnlockLoggingFile; PROCEDURE LockPersistentObjList; BEGIN {EXCLUSIVE} AWAIT(~lockPersList); lockPersList := TRUE END LockPersistentObjList; PROCEDURE UnlockPersistentObjList; BEGIN {EXCLUSIVE} lockPersList := FALSE END UnlockPersistentObjList; PROCEDURE CompactLogFile; VAR file, newfile: Files.File; doc: XML.Document; root, elem: XML.Element; enum: XMLObjects.Enumerator; p: ANY; oidString, savingCounterString: Strings.String; i, oid, savingCounter: LONGINT; wrapper: PersistentObjectWrapper; removeList: TFClasses.List; fwriter: Files.Writer; writer: Streams.Writer; elemName, rootName: Strings.String; BEGIN LockLoggingFile; file := Files.Old(LogFileName^); IF (file # NIL) THEN newfile := Files.New(LogFileName^); IF (newfile # NIL) THEN NEW(removeList); doc := GetXMLDocument(file); IF (doc # NIL) THEN root := doc.GetRoot(); rootName := root.GetName(); IF (rootName^ = XMLLogRootElemName) THEN enum := root.GetContents(); WHILE (enum.HasMoreElements()) DO p := enum.GetNext(); IF (p IS XML.Element) THEN elem := p(XML.Element); elemName := elem.GetName(); IF ((elemName^ = XMLInstanceElemName) OR (elemName^ = XMLLogDelInstElemName)) THEN oidString := elem.GetAttributeValue(XMLAttrOidName); savingCounterString := elem.GetAttributeValue(XMLAttrSavingCounter); IF ((oidString # NIL) & (savingCounterString # NIL)) THEN Strings.StrToInt(oidString^, oid); Strings.StrToInt(savingCounterString^, savingCounter); wrapper := GetRegisteredWrapperByOid(oid); IF (((wrapper # NIL) & (savingCounter < wrapper.savingCounter)) OR (wrapper = NIL)) THEN (* either the savingCounter for the log entry is stale or the object has been removed *) removeList.Add(elem) END ELSE KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': In the snapshot file '"); KernelLog.String(SnapShotFileName^); KernelLog.String("' is an instance without oid or without saving time."); KernelLog.Ln END END END END; FOR i := 0 TO removeList.GetCount()-1 DO p := removeList.GetItem(i); elem := p(XML.Element); root.RemoveContent(elem) END; Files.OpenWriter(fwriter, newfile, 0); writer := fwriter; doc.Write(writer, NIL, 0); fwriter.Update; Files.Register(newfile); IF (DEBUG) THEN KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': Log file compacted."); KernelLog.Ln END ELSE KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': Cannot overwrite the log file '"); KernelLog.String(SnapShotFileName^); KernelLog.String("' while compacting the log file."); KernelLog.Ln END ELSE KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': In the log file '"); KernelLog.String(LogFileName^); KernelLog.String("' must be a root defined as '"); KernelLog.String(XMLLogRootElemName); KernelLog.String("'."); KernelLog.Ln END; END END; UnlockLoggingFile END CompactLogFile; PROCEDURE GarbageCollect*; VAR i: LONGINT; pObj: ANY; wrapper: PersistentObjectWrapper; removeList: TFClasses.List; (* get the registered wrapper without locking the persistentObjectList, since it is already locked by outer procedure *) PROCEDURE GetWrapperForObj(obj: PersistentObject) : PersistentObjectWrapper; VAR k: LONGINT; ptr: ANY; wpr: PersistentObjectWrapper; BEGIN FOR k := 0 TO persistentObjectList.GetCount()-1 DO ptr := persistentObjectList.GetItem(k); wpr := ptr(PersistentObjectWrapper); (* wpr # NIL *) IF (wpr.instance = obj) THEN RETURN wpr END END; RETURN NIL END GetWrapperForObj; PROCEDURE MarkReachableObjects(obj: PersistentObject); VAR k: LONGINT; list: PersistentObjectList; wpr: PersistentObjectWrapper; BEGIN (* w # NIL & w.instance # NIL *) list := obj.GetReferrencedObjects(); IF (list # NIL) THEN FOR k := 0 TO LEN(list)-1 DO wpr := GetWrapperForObj(list[k]); IF (wpr # NIL) THEN IF (~wpr.isMarked) THEN wpr.isMarked := TRUE; (* cyclic referrencing possible *) MarkReachableObjects(wpr.instance) (* wpr.instance # NIL *) ELSE wpr.isMarked := TRUE END END END END END MarkReachableObjects; BEGIN LockPersistentObjList; persistentObjectList.Lock; FOR i := 0 TO persistentObjectList.GetCount()-1 DO(* unmark all objects *) pObj := persistentObjectList.GetItem(i); wrapper := pObj(PersistentObjectWrapper); wrapper.isMarked := FALSE END; (* mark phase *) FOR i := 0 TO persistentObjectList.GetCount()-1 DO pObj := persistentObjectList.GetItem(i); wrapper := pObj(PersistentObjectWrapper); IF (wrapper.isRoot) THEN (* start from a root persistent object *) wrapper.isMarked := TRUE; MarkReachableObjects(wrapper.instance) END END; (* detect garbage *) NEW(removeList); FOR i := 0 TO persistentObjectList.GetCount()-1 DO pObj := persistentObjectList.GetItem(i); wrapper := pObj(PersistentObjectWrapper); IF (~wrapper.isMarked) THEN IF (DEBUG) THEN KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': Garbage collector: Free object with oid "); KernelLog.Int(wrapper.instance.oid, 0); KernelLog.String(" "); KernelLog.String(wrapper.descriptor.moduleName^); KernelLog.String("."); KernelLog.String(wrapper.descriptor.objectName^); KernelLog.Ln END; removeList.Add(wrapper) END END; persistentObjectList.Unlock; (* sweep phase *) FOR i := 0 TO removeList.GetCount()-1 DO pObj := removeList.GetItem(i); persistentObjectList.Remove(pObj) END; UnlockPersistentObjList END GarbageCollect; PROCEDURE PersistAllObjects; (* store a snapshot of the prevalence system to an XML file *) VAR fw: Files.Writer; w: Streams.Writer; newFile: Files.File; newRoot, elem: XML.Element; i: LONGINT; pObj: ANY; wrapper: PersistentObjectWrapper; instance: PersistentObject; oldDocument: XML.Document; oidCounterString: ARRAY 14 OF CHAR; PROCEDURE GetPreviousSnapShotState(oid: LONGINT) : XML.Element; VAR file: Files.File; oldRoot: XML.Element; enum: XMLObjects.Enumerator; pOldElem: ANY; oldElem: XML.Element; oidValue: Strings.String; oldOid: LONGINT; oldRootName, oldElemName: Strings.String; BEGIN (* file # NIL *) IF (oldDocument = NIL) THEN file := Files.Old(SnapShotFileName^); IF (file # NIL) THEN oldDocument := GetXMLDocument(file) END; END; IF (oldDocument # NIL) THEN oldRoot := oldDocument.GetRoot(); oldRootName := oldRoot.GetName(); IF ((oldRoot # NIL) & (oldRootName^ = XMLRootElemName)) THEN enum := oldRoot.GetContents(); WHILE (enum.HasMoreElements()) DO pOldElem := enum.GetNext(); IF (pOldElem IS XML.Element) THEN oldElem := pOldElem(XML.Element); oldElemName := oldElem.GetName(); IF (oldElemName^ = XMLInstanceElemName) THEN oidValue := oldElem.GetAttributeValue(XMLAttrOidName); IF (oidValue # NIL) THEN Strings.StrToInt(oidValue^, oldOid); IF (oldOid = oid) THEN RETURN oldElem END ELSE KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': In the snapshot file '"); KernelLog.String(SnapShotFileName^); KernelLog.String("' is an instance without attribute 'oid'."); KernelLog.Ln END END END END ELSE KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': In the snapshot file '"); KernelLog.String(SnapShotFileName^); KernelLog.String("' must be a root defined as '"); KernelLog.String(XMLRootElemName); KernelLog.String("'."); KernelLog.Ln END ELSE KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': Could not parse the snapshot file '"); KernelLog.String(SnapShotFileName^); KernelLog.String("' during taking a snapshot of the system."); KernelLog.Ln END; (* it could be that the persistent object was not present at the last snapshot time *) RETURN NIL END GetPreviousSnapShotState; BEGIN oldDocument := NIL; LockSnapShotFile; newFile := Files.New(SnapShotFileName^); IF (newFile # NIL) THEN Strings.IntToStr(oidCounter, oidCounterString); NEW(newRoot); newRoot.SetName(XMLRootElemName); newRoot.SetAttributeValue(XMLOidCounterAttrName, oidCounterString); GarbageCollect; persistentObjectList.Lock; FOR i := 0 TO persistentObjectList.GetCount()-1 DO pObj := persistentObjectList.GetItem(i); wrapper := pObj(PersistentObjectWrapper); (* wrapper # NIL & wrapper.instance # NIL *) IF (IsModuleLoaded(wrapper.descriptor.moduleName^)) THEN instance := wrapper.instance; instance.takingSnapShot := TRUE; IF (~instance.inModification) THEN INC(wrapper.savingCounter); elem := GetSerializedXMLInstance(wrapper); instance.takingSnapShot := FALSE; newRoot.AddContent(elem) ELSE (* Is in transaction, take the previous version if present, could be recovered from log by next recovery *) instance.takingSnapShot := FALSE; elem := GetPreviousSnapShotState(instance.oid); IF (elem # NIL) THEN (* object was already present at the last snapshot time *) newRoot.AddContent(elem) END END ELSE (* Snapshot no more possible since module has been freed *) KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': module '"); KernelLog.String(wrapper.descriptor.moduleName^); KernelLog.String("' has been freed. Taking snapshot is no further possible, the system uses now only logging."); snapShotMgr.alive := FALSE; RETURN END END; persistentObjectList.Unlock; Files.OpenWriter(fw, newFile, 0); w := fw; w.String(XMLProlog); w.Ln; newRoot.Write(w, NIL, 0); fw.Update; Files.Register(newFile); IF (DEBUG) THEN KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': Snapshot stored to "); KernelLog.String(SnapShotFileName^); KernelLog.Ln END ELSE KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': Cannot create or overwrite file '"); KernelLog.String(SnapShotFileName^); KernelLog.String("' for storing the snapshot."); KernelLog.Ln END; UnlockSnapShotFile; (* now reduce redundant entries from the log file *) CompactLogFile; END PersistAllObjects; PROCEDURE GetSerializedXMLInstance(wrapper: PersistentObjectWrapper) : XML.Element; VAR content: XML.Content; elem: XML.Element; savingCounterString, oidString: ARRAY 14 OF CHAR; container: XML.Container; instance: PersistentObject; desc: PersistentObjectDescriptor; enum: XMLObjects.Enumerator; pChild: ANY; child: XML.Content; BEGIN instance := wrapper.instance; desc := wrapper.descriptor; (* here would be an exception handler fine*) content := instance.Externalize(); (* the instance externalization is locked while a transaction is done *) Strings.IntToStr(wrapper.savingCounter, savingCounterString); Strings.IntToStr(instance.oid, oidString); NEW(elem); elem.SetName(XMLInstanceElemName); elem.SetAttributeValue(XMLAttrModuleName, desc.moduleName^); elem.SetAttributeValue(XMLAttrObjectName, desc.objectName^); elem.SetAttributeValue(XMLAttrOidName, oidString); elem.SetAttributeValue(XMLAttrSavingCounter, savingCounterString); IF (wrapper.isRoot) THEN elem.SetAttributeValue(XMLAttrIsRootName, "true") END; IF ((content # NIL) & (content IS XML.Container) & (~(content IS XML.Element))) THEN (* it is a simple container *) container := content(XML.Container); enum := container.GetContents(); WHILE (enum.HasMoreElements()) DO pChild := enum.GetNext(); child := pChild(XML.Content); elem.AddContent(child) END ELSIF (content # NIL) THEN elem.AddContent(content) END; RETURN elem END GetSerializedXMLInstance; PROCEDURE GetXMLInstanceDeletion(wrapper: PersistentObjectWrapper) : XML.Element; VAR instance: PersistentObject; desc: PersistentObjectDescriptor; elem: XML.Element; savingCounterString, oidString: ARRAY 14 OF CHAR; BEGIN instance := wrapper.instance; (* instance # NIL *) desc := wrapper.descriptor; (* desc # NIL *) Strings.IntToStr(wrapper.savingCounter, savingCounterString); Strings.IntToStr(instance.oid, oidString); NEW(elem); elem.SetName(XMLLogDelInstElemName); elem.SetAttributeValue(XMLAttrModuleName, desc.moduleName^); elem.SetAttributeValue(XMLAttrObjectName, desc.objectName^); elem.SetAttributeValue(XMLAttrOidName, oidString); elem.SetAttributeValue(XMLAttrSavingCounter, savingCounterString); RETURN elem END GetXMLInstanceDeletion; PROCEDURE LogXMLElement(elem: XML.Element); VAR file: Files.File; fwriter: Files.Writer; writer: Streams.Writer; endPos, endTagLength: LONGINT; BEGIN LockLoggingFile; file := Files.Old(LogFileName^); IF (file = NIL) THEN file := Files.New(LogFileName^); Files.Register(file) END; IF (file # NIL) THEN (* Don't use the XML parser, it's too inefficient. Append the XML serialized state an the end of the xml file *) endTagLength := Strings.Length(XMLLogRootElemName)+5; (* LEN(""+CR+LF *) IF (file.Length()-endTagLength-Strings.Length(XMLProlog)-2 <= 0) THEN (* empty file or only in it *) Files.OpenWriter(fwriter, file, 0); writer := fwriter; writer.String(XMLProlog); writer.Ln; writer.String("<"); writer.String(XMLLogRootElemName); writer.String(">"); writer.Ln; (* opening tag "" *) ELSE endPos := file.Length()-endTagLength; ASSERT(endPos >= 0, 9999); Files.OpenWriter(fwriter, file, endPos); writer := fwriter END; elem.Write(writer, NIL, 0); writer.Ln; writer.String(""); writer.Ln; writer.Update ELSE KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': Cannot open or create file '"); KernelLog.String(LogFileName^); KernelLog.String("'."); KernelLog.Ln; UnlockLoggingFile; HALT(9999); (* Cannot commit the transaction, could support "ABORT" at a later time *) END; UnlockLoggingFile END LogXMLElement; PROCEDURE Log(obj: PersistentObject); VAR elem: XML.Element; wrapper: PersistentObjectWrapper; BEGIN wrapper := GetRegisteredWrapper(obj); IF (wrapper # NIL) THEN elem := GetSerializedXMLInstance(wrapper); LogXMLElement(elem) ELSE KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': The object with oid '"); KernelLog.Int(obj.oid, 0); KernelLog.String("' is not stored in the prevalence system and will therefore not be logged."); KernelLog.Ln END END Log; PROCEDURE LogRemovalFromRootSet(wrapper: PersistentObjectWrapper); VAR elem: XML.Element; BEGIN IF (wrapper # NIL) THEN elem := GetXMLInstanceDeletion(wrapper); LogXMLElement(elem) ELSE KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': The object with is not stored in the prevalence system and will therefore not be logged."); KernelLog.Ln END END LogRemovalFromRootSet; (* The last oid counter can be restored by the max(oidCounter in the snapshot file, max(oid of all logged object)+1) For all objects which were created before the last snapshot was taken, the oidCounter in the snapshot file is greater the oid for this objects. If an object O was created after the last snapshot was taken and O has been registered in the prevalence system for a time (even if it was later removed) then by looking at the maximum oid occurring in the log file the oidCounter is chosen greater than the oid of O. If an object O was created before the last snapshot was taken and O has never been registered in the prevalence system then O will be destroyed if the module PrevalenceSystem is freed, since each module M using O imports the module PrevalenceSystem. Hence O will not affect the oid uniqueness condition at the next incarnation time for the prevalence system. *) PROCEDURE RestoreAllObjects; VAR snapShotFile, logFile: Files.File; snapShotDoc, logDoc: XML.Document; snapShotRoot, logRoot, elem: XML.Element; enum: XMLObjects.Enumerator; p: ANY; moduleName, objectName, oidString, savingCounterString, isRootString: Strings.String; oid, savingCounter: LONGINT; isRoot: BOOLEAN; snapShotRootName, logRootName, elemName, oidCounterString: Strings.String; desc: PersistentObjectDescriptor; objWrapper: PersistentObjectWrapper; PROCEDURE CreatePersistentObject; VAR persObj: PersistentObject; wrapper: PersistentObjectWrapper; BEGIN IF (desc # NIL) THEN (* persistent object serializations could occur multiple times in log file *) IF (GetPersistentObject(oid) = NIL) THEN (* first time that the persistent object occurs *) (* Always overwriting savingCounter and isRoot would lead to inconsistent states since there could be * stale informations in the log file *) persObj := desc.factory(); IF (persObj # NIL) THEN persObj.oid := oid; NEW(wrapper, SELF, persObj, desc); wrapper.savingCounter := savingCounter; wrapper.isRoot := isRoot; persistentObjectList.Add(wrapper); persObj.registeredAt := SELF ELSE KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': cannot create an instance of the persistent object '"); KernelLog.String(objectName^); KernelLog.String("' in module '"); KernelLog.String(moduleName^); KernelLog.String("'."); KernelLog.Ln; HALT(9999) END END ELSE KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': persistent object '"); KernelLog.String(objectName^); KernelLog.String("' in module '"); KernelLog.String(moduleName^); KernelLog.String("' must be installed because it is stored in the snapshot or log file."); KernelLog.Ln; HALT(9999) END END CreatePersistentObject; PROCEDURE RestorePersistentObject; VAR pContent: ANY; content: XML.Content; contentEnum: XMLObjects.Enumerator; contentList: TFClasses.List; persObj: PersistentObject; container: XML.Container; j: LONGINT; BEGIN persObj := GetPersistentObject(oid); IF (persObj # NIL) THEN contentEnum := elem.GetContents(); NEW(contentList); WHILE (contentEnum.HasMoreElements()) DO pContent := contentEnum.GetNext(); contentList.Add(pContent) END; IF (contentList.GetCount() = 0) THEN content := NIL ELSIF (contentList.GetCount() = 1) THEN pContent := contentList.GetItem(0); content := pContent(XML.Content) ELSE NEW(container); FOR j := 0 TO contentList.GetCount()-1 DO pContent := contentList.GetItem(j); content := pContent(XML.Content); container.AddContent(content) END; content := container END; (* here would be an exception handler fine *) persObj.Internalize(content); ELSE KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': Recovery process: there is no object with oid '"); KernelLog.Int(oid, 0); KernelLog.String("' present."); KernelLog.Ln END END RestorePersistentObject; PROCEDURE AllocatePersistentObjects(root: XML.Element); VAR contentEnum: XMLObjects.Enumerator; BEGIN contentEnum := root.GetContents(); WHILE (contentEnum.HasMoreElements()) DO p := contentEnum.GetNext(); IF (p IS XML.Element) THEN elem := p(XML.Element); elemName := elem.GetName(); IF (elemName^ = XMLInstanceElemName) THEN moduleName := elem.GetAttributeValue(XMLAttrModuleName); objectName := elem.GetAttributeValue(XMLAttrObjectName); oidString := elem.GetAttributeValue(XMLAttrOidName); savingCounterString := elem.GetAttributeValue(XMLAttrSavingCounter); isRootString := elem.GetAttributeValue(XMLAttrIsRootName); IF ((moduleName # NIL) & (objectName # NIL) & (oidString # NIL) & (savingCounterString # NIL)) THEN Strings.StrToInt(oidString^, oid); Strings.StrToInt(savingCounterString^, savingCounter); IF ((isRootString # NIL) & (isRootString^ = "true")) THEN isRoot := TRUE ELSE isRoot := FALSE END; IF (oid >= oidCounter) THEN oidCounter := oid + 1 END; desc := FindRegisteredDescriptor(moduleName^, objectName^); (* savingCounter and isRoot etc. is only set in CreatePersistentObject if the object occurs the first time. * Always overwriting this information would lead to inconsistent states since there could be * stale informations in the log file. *) CreatePersistentObject ELSE KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': There are instances with missing attributes in the snapshot or log file."); KernelLog.Ln; UnlockLoggingFile; UnlockSnapShotFile; UnlockPersistentObjList; HALT(9999) END END END END END AllocatePersistentObjects; BEGIN LockPersistentObjList; LockSnapShotFile; LockLoggingFile; (* two phases: first create all persistent object instances, then invoke the internalization methods. This allows that the persistent can have references to other persistent object instances. *) (* first phase: create the persistent object instances. First consider all objects in the snapshot file then * the new objects only reported in the log file. *) snapShotFile := Files.Old(SnapShotFileName^); IF (snapShotFile # NIL) THEN snapShotDoc := GetXMLDocument(snapShotFile); IF (snapShotDoc # NIL) THEN snapShotRoot := snapShotDoc.GetRoot(); snapShotRootName := snapShotRoot.GetName(); IF (snapShotRootName^ = XMLRootElemName) THEN oidCounterString := snapShotRoot.GetAttributeValue(XMLOidCounterAttrName); IF (oidCounterString # NIL) THEN Strings.StrToInt(oidCounterString^, oidCounter); AllocatePersistentObjects(snapShotRoot) ELSE snapShotRoot := NIL; KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': In the snapshot file '"); KernelLog.String(SnapShotFileName^); KernelLog.String("' the root '"); KernelLog.String(XMLRootElemName); KernelLog.String("' must have an attribute named'"); KernelLog.String(XMLOidCounterAttrName); KernelLog.String("'."); KernelLog.Ln END ELSE snapShotRoot := NIL; KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': In the snapshot file '"); KernelLog.String(SnapShotFileName^); KernelLog.String("' must be a root defined as '"); KernelLog.String(XMLRootElemName); KernelLog.String("'."); KernelLog.Ln END ELSE (* error message already handled by GetXMLDocument *) UnlockLoggingFile; UnlockSnapShotFile; UnlockPersistentObjList; HALT(9999) END END; logFile := Files.Old(LogFileName^); IF (logFile # NIL) THEN logDoc := GetXMLDocument(logFile); IF (logDoc # NIL) THEN logRoot := logDoc.GetRoot(); logRootName := logRoot.GetName(); IF (logRootName^ = XMLLogRootElemName) THEN AllocatePersistentObjects(logRoot) ELSE logRoot := NIL; KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': In the log file '"); KernelLog.String(LogFileName^); KernelLog.String("' must be a root defined as '"); KernelLog.String(XMLLogRootElemName); KernelLog.String("'."); KernelLog.Ln END ELSE (* error message already handled by GetXMLDocument *) UnlockLoggingFile; UnlockSnapShotFile; UnlockPersistentObjList; HALT(9999) END END; (* second phase: internalize persistent object state *) IF (snapShotRoot # NIL) THEN enum := snapShotRoot.GetContents(); WHILE (enum.HasMoreElements()) DO p := enum.GetNext(); IF (p IS XML.Element) THEN elem := p(XML.Element); elemName := elem.GetName(); IF (elemName^ = XMLInstanceElemName) THEN oidString := elem.GetAttributeValue(XMLAttrOidName); IF (oidString # NIL) THEN Strings.StrToInt(oidString^, oid); (* the savingCounter and isRoot are consistent, since the object was created by the information * of the snapshot file and savingCounter and isRoot were not overwritten by the log file until now *) RestorePersistentObject ELSE KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': There are object instances with missing oid in the snapshot file."); KernelLog.Ln END END END END; IF (DEBUG) THEN KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': Recovery from snapshot done."); KernelLog.Ln END END; IF (logRoot # NIL) THEN enum := logRoot.GetContents(); WHILE (enum.HasMoreElements()) DO p := enum.GetNext(); IF (p IS XML.Element) THEN elem := p(XML.Element); elemName := elem.GetName(); IF ((elemName^ = XMLInstanceElemName) OR (elemName^ = XMLLogDelInstElemName)) THEN oidString := elem.GetAttributeValue(XMLAttrOidName); savingCounterString := elem.GetAttributeValue(XMLAttrSavingCounter); IF ((oidString # NIL) & (savingCounterString # NIL)) THEN Strings.StrToInt(oidString^, oid); Strings.StrToInt(savingCounterString^, savingCounter); objWrapper := GetRegisteredWrapperByOid(oid); (* objWrapper # NIL since they were previously created *) IF ((objWrapper # NIL) & (elemName^ = XMLInstanceElemName)) THEN isRootString := elem.GetAttributeValue(XMLAttrIsRootName); IF ((isRootString # NIL) & (isRootString^ = "true")) THEN isRoot := TRUE ELSE isRoot := FALSE END; IF (savingCounter >= objWrapper.savingCounter) THEN (* only update if newer information *) objWrapper.savingCounter := savingCounter; objWrapper.isRoot := isRoot; RestorePersistentObject END ELSIF ((objWrapper # NIL) & (elemName^ = XMLLogDelInstElemName)) THEN IF (savingCounter >= objWrapper.savingCounter) THEN (* only update if newer information *) objWrapper.isRoot := FALSE (* the object doesn't belong anymore to the root set, and could be removed later by * the garbage collector of the prevalence system *) END ELSE KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': Recovery phase: Object with oid '"); KernelLog.Int(oid, 0); KernelLog.String("' is not present."); KernelLog.Ln END ELSE KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': There are object instances with missing oid or saving counter in the log file."); KernelLog.Ln END END END END; IF (DEBUG) THEN KernelLog.String("Prevalence System '"); KernelLog.String(SystemName^); KernelLog.String("': Recovery from log file done."); KernelLog.Ln END END; UnlockLoggingFile; UnlockSnapShotFile; UnlockPersistentObjList END RestoreAllObjects; PROCEDURE GetRegisteredWrapperByOid(oid: LONGINT) : PersistentObjectWrapper; VAR i: LONGINT; p: ANY; wrapper: PersistentObjectWrapper; BEGIN persistentObjectList.Lock; FOR i := 0 TO persistentObjectList.GetCount()-1 DO p := persistentObjectList.GetItem(i); wrapper := p(PersistentObjectWrapper); (* wrapper # NIL & wrapper.instance # NIL *) IF (wrapper.instance.oid = oid) THEN persistentObjectList.Unlock; RETURN wrapper END END; persistentObjectList.Unlock; RETURN NIL END GetRegisteredWrapperByOid; PROCEDURE GetRegisteredWrapper(obj: PersistentObject) : PersistentObjectWrapper; VAR i: LONGINT; p: ANY; wrapper: PersistentObjectWrapper; BEGIN IF (obj # NIL) THEN persistentObjectList.Lock; FOR i := 0 TO persistentObjectList.GetCount()-1 DO p := persistentObjectList.GetItem(i); wrapper := p(PersistentObjectWrapper); (* wrapper # NIL *) IF (wrapper.instance = obj) THEN persistentObjectList.Unlock; RETURN wrapper END END; persistentObjectList.Unlock END; RETURN NIL END GetRegisteredWrapper; END PrevalenceSystem; VAR prevSystemList: TFClasses.List; (* List of Prevelence System *) standardPrevalenceSystem*: PrevalenceSystem; persistentObjectDescs: TFClasses.List; (* List of PersistentObjectDescriptor *) snapShotMgr: SnapShotManager; (* singleton *) (* error handling mechanism for XML Parser and Scanner *) xmlParserErrorMsg: ARRAY 1024 OF CHAR; xmlParserErrorOccurred: BOOLEAN; lockParsingScanning: BOOLEAN; lockPrevSystemList: BOOLEAN; PROCEDURE GetPrevalenceSystem*(CONST name: ARRAY OF CHAR) : PrevalenceSystem; VAR i: LONGINT; p: ANY; prevSys: PrevalenceSystem; BEGIN prevSystemList.Lock; FOR i := 0 TO prevSystemList.GetCount()-1 DO p := prevSystemList.GetItem(i); prevSys := p(PrevalenceSystem); IF (prevSys.SystemName^ = name) THEN prevSystemList.Unlock; RETURN prevSys END END; prevSystemList.Unlock; RETURN NIL END GetPrevalenceSystem; (** operations on the standard prevalence system *) (** each persistent object has to be registered in the standard prevalence system to make sure that its descriptor is known. * This does not affect that the object will be collected as garbage if it is not reachable through a root persistent object *) PROCEDURE AddPersistentObject*(obj: PersistentObject; desc: PersistentObjectDescriptor); BEGIN standardPrevalenceSystem.AddPersistentObject(obj, desc) END AddPersistentObject; (** add object to the root set of the standard prevalence system. This objects must be manually removed from the prevalence system, * All objects reached by a root persistent object are also persistent *) PROCEDURE AddPersistentObjectToRootSet*(obj: PersistentObject; desc: PersistentObjectDescriptor); BEGIN standardPrevalenceSystem.AddPersistentObjectToRootSet(obj, desc) END AddPersistentObjectToRootSet; (** the object will be marked to be no more belonging to the root set of the standard prevalence system * and all persistent objects only reachable by this object will be removed in the next garbage collection phase *) PROCEDURE RemovePersistentRootObject*(obj: PersistentObject); BEGIN standardPrevalenceSystem.RemovePersistentRootObject(obj) END RemovePersistentRootObject; PROCEDURE GetPersistentObject*(oid: LONGINT): PersistentObject; BEGIN RETURN standardPrevalenceSystem.GetPersistentObject(oid) END GetPersistentObject; PROCEDURE GetDescriptorByObject*(obj: PersistentObject) : PersistentObjectDescriptor; BEGIN RETURN standardPrevalenceSystem.GetDescriptorByObject(obj) END GetDescriptorByObject; PROCEDURE FindPersistentObjects*(pred: FilterPredicate) : PersistentObjectList; BEGIN RETURN standardPrevalenceSystem.FindPersistentObjects(pred) END FindPersistentObjects; (** end of operations on the standard prevalence system *) PROCEDURE LockPrevSystemList; BEGIN {EXCLUSIVE} AWAIT(~lockPrevSystemList); lockPrevSystemList := TRUE END LockPrevSystemList; PROCEDURE UnlockPrevSystemList; BEGIN {EXCLUSIVE} lockPrevSystemList := FALSE END UnlockPrevSystemList; PROCEDURE LockParsingScanning; BEGIN {EXCLUSIVE} AWAIT(~lockParsingScanning); lockParsingScanning := TRUE END LockParsingScanning; PROCEDURE UnlockParsingScanning; BEGIN {EXCLUSIVE} lockParsingScanning := FALSE END UnlockParsingScanning; PROCEDURE ReportXMLParserScannerError(pos, line, row: LONGINT; CONST msg: ARRAY OF CHAR); (* Error handler for the XML parser *) VAR w: Streams.StringWriter; BEGIN xmlParserErrorOccurred := TRUE; NEW(w, LEN(xmlParserErrorMsg)); w.String(msg); w.String(" pos "); w.Int(pos, 0); w.String("line "); w.Int(line, 0); w.String("row "); w.Int(row, 0); w.Ln; w.Get(xmlParserErrorMsg) END ReportXMLParserScannerError; PROCEDURE IsModuleLoaded(CONST modName: ARRAY OF CHAR) : BOOLEAN; VAR module: Modules.Module; BEGIN module := Modules.ModuleByName(modName); RETURN (module # NIL) END IsModuleLoaded; PROCEDURE FindRegisteredDescriptor(CONST moduleName, objectName: ARRAY OF CHAR) : PersistentObjectDescriptor; VAR p: ANY; i: LONGINT; desc: PersistentObjectDescriptor; BEGIN persistentObjectDescs.Lock; FOR i := 0 TO persistentObjectDescs.GetCount()-1 DO p := persistentObjectDescs.GetItem(i); desc := p(PersistentObjectDescriptor); IF ((desc.moduleName^ = moduleName) & (desc.objectName^ = objectName)) THEN persistentObjectDescs.Unlock; RETURN desc END END; persistentObjectDescs.Unlock; RETURN NIL END FindRegisteredDescriptor; PROCEDURE RegisterDescriptor(desc: PersistentObjectDescriptor); VAR pos: LONGINT; BEGIN {EXCLUSIVE} IF (desc # NIL) THEN persistentObjectDescs.Lock; pos := persistentObjectDescs.IndexOf(desc); persistentObjectDescs.Unlock; IF (pos = -1) THEN (* not registered yet *) persistentObjectDescs.Add(desc) END END END RegisterDescriptor; PROCEDURE ReadRegisteredModules; VAR elem, child: XML.Element; enum: XMLObjects.Enumerator; p: ANY; childName, moduleName: Strings.String; attr: XML.Attribute; BEGIN IF (Configuration.config # NIL) THEN elem := Configuration.config.GetRoot(); elem := Configuration.GetNamedElement(elem, "Section", ConfigurationSupperSectionName); IF (elem # NIL) THEN elem := Configuration.GetNamedElement(elem, "Section", ConfigurationSubSectionName); IF (elem # NIL) THEN enum := elem.GetContents(); WHILE (enum.HasMoreElements()) DO p := enum.GetNext(); IF (p IS XML.Element) THEN child := p(XML.Element); childName := child.GetName(); IF (childName^ = "Setting") THEN attr := child.GetAttribute("value"); IF (attr # NIL) THEN moduleName := attr.GetValue(); RegisterModuleByName(moduleName) END END END END ELSE KernelLog.String("Prevalence System: In Configuration.XML under '"); KernelLog.String(ConfigurationSupperSectionName); KernelLog.String("' is no section '"); KernelLog.String(ConfigurationSubSectionName); KernelLog.String(" defined."); KernelLog.Ln END ELSE KernelLog.String("Prevalence System: In Configuration.XML is no section '"); KernelLog.String(ConfigurationSupperSectionName); KernelLog.String("' defined."); KernelLog.Ln END ELSE KernelLog.String("Prevalence System: Cannot open Configuration.XML"); KernelLog.Ln END END ReadRegisteredModules; PROCEDURE RegisterModuleByName(moduleName: Strings.String); VAR module: Modules.Module; factory : PersistentObjectDescSetFactory; i: LONGINT; res: WORD; msg: ARRAY 1024 OF CHAR; desc: PersistentObjectDescriptor; descList: PersistentObjectDescSet; BEGIN (* load the module if not already loaded *) module := Modules.ThisModule(moduleName^, res, msg); IF ((res = 0) & (module # NIL)) THEN GETPROCEDURE(moduleName^, ProcNameGetDescriptors, factory); IF (factory # NIL) THEN descList := factory(); IF (descList # NIL) THEN (* register all present descriptors *) FOR i := 0 TO descList.GetCount()-1 DO desc := descList.GetItem(i); RegisterDescriptor(desc) END ELSE KernelLog.String("System pervalence: Wrong result type from procedure '"); KernelLog.String(ProcNameGetDescriptors); KernelLog.String("' in module '"); KernelLog.String(moduleName^); KernelLog.String("'"); KernelLog.Ln END ELSE KernelLog.String("System prevalence: Procedure '"); KernelLog.String(ProcNameGetDescriptors); KernelLog.String("' in module '"); KernelLog.String(moduleName^); KernelLog.String("' is not present."); KernelLog.Ln END ELSE KernelLog.String("System prevalence: Module '"); KernelLog.String(moduleName^); KernelLog.String("' is not present."); KernelLog.Ln; KernelLog.String(msg); KernelLog.Ln END END RegisterModuleByName; PROCEDURE Terminator; VAR counter: LONGINT; BEGIN IF (snapShotMgr # NIL) THEN snapShotMgr.alive := FALSE; snapShotMgr.timer.Wakeup; counter := 0; WHILE ((~snapShotMgr.terminated) & (counter < TermTimeout)) DO INC(counter) END (* busy wait until snapShotMgr has stopped, avoid permanent system blocking by a timeout *) END END Terminator; BEGIN NEW(persistentObjectDescs); NEW(prevSystemList); lockParsingScanning := FALSE; (* reconstruct the prevalence systems *) ReadRegisteredModules; NEW(standardPrevalenceSystem, StandardPrevSystemName, StandardSnapShotFileName, StandardLogFileName); NEW(snapShotMgr); Modules.InstallTermHandler(Terminator) END PrevalenceSystem.