As of the writing of this blog entry (December 2006) the BlueBottle/AOS operating system does not support any SQL based database systems such as Oracle or MySQL. So that might lead someone to wonder "just how can you build a database app without a database"? Good question. Actually BlueBottle does currently come with it's own XML powered persistent object database system known as a prevalence system. Prevalence systems are not unique to BlueBottle and implementations exist for Java, Lisp and other languages. The basic idea is that you use objects directly as the database as opposed to connecting them to a traditional RDMS. Objects have to be serializable, meaning that each object needs methods to externalize to and internalize from a representation that can be written directly to disk. Prevalence systems support transactions and crash recovery through the use of a snapshot and a transaction log.
BlueBottle's prevalency system facility is not well documented as of this writing. In fact you might never realize it's there unless you look into the "DynamicWebPage" (dxp) facility documented in the file DynamicWebpagePlugin.Text. The .dxp pages use an XML template language to serve dynamic content. But some .dxp content is persistent such as visitor counters. The prevalence system is used to handle this.
First Steps
Before diving into building a complete database app I needed to know the basics of getting a prevalence system to work under BlueBottle. The .dxp examples were not much help in this because the have the prevalance code and the dynamic web code in one module. That was too much information for me to handle on the first cut. I needed the "Hello world" equivalent of a prevalence database system. Thankfully Luc Bläser gave me some hints on the BlueBottle mailing list. From those hints I was able to create the following modules for implementing and testing a simple prevalence system.
MODULE MyPersonModule;
IMPORT
PS := PrevalenceSystem, XML, WebStd, Utilities, AosOut;
TYPE
Person* = OBJECT(PS.PersistentObject)
VAR name-: ARRAY 40 OF CHAR;
PROCEDURE Externalize(): XML.Content;
BEGIN RETURN WebStd.CreateXMLText(name)
END Externalize;
PROCEDURE Internalize(xml: XML.Content);
VAR s: Utilities.String;
BEGIN s := WebStd.GetXMLCharContent(xml(XML.Container))
END Internalize;
PROCEDURE GetReferrencedObjects*() : PS.PersistentObjectList;
BEGIN RETURN NIL
END GetReferrencedObjects;
PROCEDURE SetName*(s : ARRAY OF CHAR);
BEGIN
AosOut.String("Setting Name : "); AosOut.String(s); AosOut.Ln;
BeginModification;
COPY(s, name);
EndModification;
END SetName;
END Person;
(* implementation of the above object type Person *)
VAR personTypeDesc : PS.PersistentObjectDescriptor;
PROCEDURE AddPersistentPerson*(s : ARRAY OF CHAR);
VAR
person : Person;
p : PTR;
descset: PS.PersistentObjectDescSet;
BEGIN
NEW(person);
PS.AddPersistentObjectToRootSet(person, personTypeDesc);
person.SetName(s);
END AddPersistentPerson;
PROCEDURE GetPersistentObjectDescriptors*(any : PTR): PTR;
VAR descSet : PS.PersistentObjectDescSet;
descs: ARRAY 1 OF PS.PersistentObjectDescriptor;
BEGIN
descs[0] := personTypeDesc; NEW(descSet, descs); RETURN descSet
END GetPersistentObjectDescriptors;
PROCEDURE CreateNewPerson*(): PS.PersistentObject;
VAR obj: Person;
BEGIN NEW(obj); RETURN obj
END CreateNewPerson;
BEGIN
NEW(personTypeDesc, "MyPersonModule", "Person", CreateNewPerson)
END MyPersonModule.
MODULE TestPrev;
IMPORT
PS := PrevalenceSystem, AosOut, PM := MyPersonModule, AosCommands, AosIO;
PROCEDURE AddPerson*(par : ANY ):ANY;
VAR
s : AosCommands.Parameters;
sr : AosIO.StringReader;
name : ARRAY 40 OF CHAR;
BEGIN
s := par(AosCommands.Parameters); NEW(sr, LEN(s.str^)); sr.Set(s.str^);
sr.String(name);
PM.AddPersistentPerson(name);
AosOut.String(name);
AosOut.Ln;
RETURN(NIL);
END AddPerson;
PROCEDURE PersonFilter(obj: PS.PersistentObject) : BOOLEAN;
BEGIN RETURN (obj IS PM.Person)
END PersonFilter;
PROCEDURE ShowAll*(par : ANY):ANY;
VAR
l: PS.PersistentObjectList;
i : INTEGER;
BEGIN
l := PS.FindPersistentObjects(PersonFilter);
FOR i := 0 TO LEN(l-1) DO;
AosOut.String(l[i](PM.Person).name);
AosOut.Ln;
END;
RETURN(NIL);
END ShowAll;
END TestPrev.
A few things to note:
- Every prevalence system object definition should have as a minimum an externalize procedure, an internalize procedure and get referrenced objects procedure.
- Every prevalence system module should have GetPersistentObjectDescriptors procedure.
- There can be more than type of prevalence system object defined in a single module.