DIRECTORY BasicTime USING [ Now ], Convert USING [ IntFromRope ], IO, LoganBerry USING [ AttributeType, AttributeValue, DeleteEntry, Entry, Error, ErrorCode, Open, OpenDB, ReadEntry, WriteEntry ], LoganBerryRpcControl USING [ ImportInterface, UnimportInterface ], LupineRuntime USING [ BindingError ], RefID USING [ nullID ], Rope USING [ Concat, Equal, ROPE ], RPC USING [ CallFailed, CallFailure, ImportFailed ], Thrush USING [EncryptionKey, nullKey, Tune, VoiceInterval], VoiceDB USING [ ErrorCode ], VoiceUtils USING [ CurrentRName ] ; VoiceDBImpl: CEDAR PROGRAM IMPORTS BasicTime, Convert, IO, LoganBerry, LoganBerryRpcControl, LupineRuntime, Rope, RPC, VoiceUtils EXPORTS VoiceDB = { OPEN IO; VoiceDBHandle: TYPE = REF VoiceDBHandleRec; VoiceDBHandleRec: PUBLIC TYPE = RECORD [ tunesDB: LoganBerry.OpenDB_RefID.nullID, tuneRefDB: LoganBerry.OpenDB_RefID.nullID, tunesDBName: Rope.ROPE, tuneRefDBName: Rope.ROPE, instance: Rope.ROPE, imported: BOOL_FALSE ]; nullKey: Thrush.EncryptionKey _ Thrush.nullKey; Error: PUBLIC ERROR[ec: VoiceDB.ErrorCode, explanation: Rope.ROPE_NIL] = CODE; Open: PUBLIC PROC[ dbName, instance, localName: Rope.ROPE ] RETURNS [ handle: VoiceDBHandle_NIL, openEc: VoiceDB.ErrorCode_NIL, expl: Rope.ROPE ] = { ENABLE { RPC.CallFailed => []_PostRPCError[handle, why, TRUE]; Error => { openEc _ ec; expl _ explanation; GOTO ReturnAnyway; }; LoganBerry.Error => []_PostLBError[ec, explanation, TRUE, dbName]; }; handle _ NEW[VoiceDBHandleRec _ [ tunesDBName: dbName.Concat[".df"], tuneRefDBName: dbName.Concat["Refs.df"]]]; handle.instance _ instance; dbName _ handle.tunesDBName; handle.tunesDB _ OpenLB[handle: handle, dbName: handle.tunesDBName]; dbName _ handle.tuneRefDBName; handle.tuneRefDB _ OpenLB[handle: handle, dbName: handle.tuneRefDBName]; EXITS ReturnAnyway => NULL; -- return incomplete handle. }; Create: PUBLIC PROC[ handle: VoiceDBHandle, tune: INT, creator: Rope.ROPE, interval: Thrush.VoiceInterval, key: Thrush.EncryptionKey, otherAttributes: LoganBerry.Entry ] RETURNS [voiceFileID: Rope.ROPE_NIL, entry: LoganBerry.Entry_NIL] = TRUSTED { reopening: BOOL_FALSE; { ENABLE { RPC.CallFailed => IF (reopening _ PostRPCError[handle, why, reopening]) THEN RETRY; LoganBerry.Error => IF (reopening _ PostLBError[ec, explanation, reopening, handle.tunesDBName]) THEN RETRY; }; cardKey: LONG POINTER TO ARRAY[0..2) OF LONG CARDINAL=LOOPHOLE [LONG[@key]]; keyRope: Rope.ROPE = IO.PutFR["%bB %bB", IO.card[cardKey[0]], IO.card[cardKey[1]]]; voiceFileID _ IO.PutFR["%g %g", int[tune], time[BasicTime.Now[]]]; IF creator=NIL THEN creator _ VoiceUtils.CurrentRName[]; entry _ CONS[ [$vid, voiceFileID], CONS[ [$cre, creator], CONS[ [$sta, IO.PutFR["%g", int[interval.start]]], CONS[ [$num, IO.PutFR["%g", int[interval.length]]], CONS[ [$key, keyRope], otherAttributes ]]]]]; IF reopening THEN handle.tunesDB _ OpenLB[handle:handle, dbName:handle.tunesDBName]; reopening _ FALSE; LoganBerry.WriteEntry[ db: handle.tunesDB, entry: entry]; }; }; Retain: PUBLIC PROC[ handle: VoiceDBHandle, voiceFileID: Rope.ROPE, refID: Rope.ROPE, refIDType: Rope.ROPE, creator: Rope.ROPE, otherAttributes: LoganBerry.Entry ] RETURNS [entry: LoganBerry.Entry_NIL] = { reopening: BOOL_FALSE; { ENABLE { RPC.CallFailed => IF (reopening _ PostRPCError[handle, why, reopening]) THEN RETRY; LoganBerry.Error => IF (reopening _ PostLBError[ec, explanation, reopening, handle.tuneRefDBName]) THEN RETRY; }; newTim, newVid: BOOL_FALSE; IF creator=NIL THEN creator _ VoiceUtils.CurrentRName[]; IF reopening THEN handle.tuneRefDB _ OpenLB[handle: handle, dbName: handle.tuneRefDBName]; reopening _ FALSE; entry _ Query[handle, NIL, refID, refIDType].refEntry; IF entry = NIL THEN entry _ CONS[ [$rid, refID], CONS[ [$rtp, refIDType], CONS[ [$cre, creator], otherAttributes ]]]; [entry, newTim] _ IncludeAttr[entry, $tim, IO.PutFR["%g", time[BasicTime.Now[]]], TRUE]; [entry, newVid] _ IncludeAttr[entry, $vid, voiceFileID, FALSE]; IF newTim OR newVid THEN Replace[handle.tuneRefDB, $rid, entry]; }; }; IncludeAttr: PROC[ entry: LoganBerry.Entry, key: LoganBerry.AttributeType, value: LoganBerry.AttributeValue, replace: BOOL ] RETURNS [newEntry: LoganBerry.Entry, isNew: BOOL_TRUE] = { FOR e: LoganBerry.Entry _ entry, e.rest WHILE e#NIL DO IF key=e.first.type THEN { IF replace THEN { e.first.value _ value; RETURN[entry, TRUE]; }; IF value#NIL AND value.Equal[e.first.value, FALSE] THEN RETURN[entry, FALSE]; }; ENDLOOP; newEntry _ CONS[ [key, value], entry]; }; Forget: PUBLIC PROC[ handle: VoiceDBHandle, refID: Rope.ROPE, refIDType: Rope.ROPE ] = { reopening: BOOL_FALSE; { ENABLE { RPC.CallFailed => IF (reopening _ PostRPCError[handle, why, reopening]) THEN RETRY; LoganBerry.Error => IF (reopening _ PostLBError[ec, explanation, reopening, handle.tuneRefDBName]) THEN RETRY; }; IF reopening THEN handle.tuneRefDB _ OpenLB[handle: handle, dbName: handle.tuneRefDBName]; reopening _ FALSE; LoganBerry.DeleteEntry[db: handle.tuneRefDB, key: $rid, value: refID! LoganBerry.Error => IF ec=$NoIndex THEN CONTINUE]; }; }; Query: PUBLIC PROC [ handle: VoiceDBHandle, voiceFileID: Rope.ROPE, refID: Rope.ROPE, refIDType: Rope.ROPE ] RETURNS [ tune: Thrush.Tune_-1, interval: Thrush.VoiceInterval, key: Thrush.EncryptionKey_Thrush.nullKey, voiceEntry: LoganBerry.Entry_NIL, refEntry: LoganBerry.Entry_NIL ] = TRUSTED { reopening: BOOL_FALSE; { ENABLE { RPC.CallFailed => IF (reopening _ PostRPCError[handle, why, reopening]) THEN RETRY; LoganBerry.Error => IF (reopening _ PostLBError[ec, explanation, reopening, handle.tuneRefDBName]) THEN RETRY; }; cardKey: LONG POINTER TO ARRAY[0..2) OF LONG CARDINAL=LOOPHOLE[LONG[@key]]; keyStream: IO.STREAM; IF (refID#NIL AND refIDType=NIL) OR ((refID=NIL) = (voiceFileID=NIL)) THEN Error[$clientError, "\nQuery specifies both refID and voiceFileID, or refIDType omitted."]; IF refID#NIL THEN { IF reopening THEN handle.tuneRefDB _OpenLB[handle: handle, dbName:handle.tuneRefDBName]; reopening _ FALSE; refEntry _ LoganBerry.ReadEntry[db: handle.tuneRefDB, key: $rid, value: refID! LoganBerry.Error => IF ec=$NoIndex THEN CONTINUE].entry; IF refEntry=NIL THEN RETURN; IF ~GetAttr[refEntry, $rtp].Equal[refIDType] THEN Error[$clientError, "\nWrong refIDType specified."]; voiceFileID _ GetAttr[refEntry, $vid]; }; { -- Now refID scene is OK; separate LoganBerry error management for voiceFileID ENABLE LoganBerry.Error => IF (reopening _ PostLBError[ec, explanation, reopening, handle.tunesDBName]) THEN RETRY; IF voiceFileID=NIL THEN RETURN; IF reopening THEN handle.tunesDB_OpenLB[handle: handle, dbName: handle.tunesDBName]; reopening _ FALSE; voiceEntry _ LoganBerry.ReadEntry[db: handle.tunesDB, key: $vid, value: voiceFileID! LoganBerry.Error => IF ec=$NoIndex THEN CONTINUE].entry; IF voiceEntry=NIL THEN RETURN; tune _ Convert.IntFromRope[voiceFileID]; interval _ [ Convert.IntFromRope[GetAttr[voiceEntry, $sta]], Convert.IntFromRope[GetAttr[voiceEntry, $num]]]; keyStream _ IO.RIS[GetAttr[voiceEntry, $key]]; cardKey[0] _ IO.GetCard[keyStream]; cardKey[1] _ IO.GetCard[keyStream]; }; }; }; Replace: PROC[ db: LoganBerry.OpenDB, key: LoganBerry.AttributeType, entry: LoganBerry.Entry] = { LoganBerry.WriteEntry[ db: db, entry: entry! LoganBerry.Error => IF ec=$ValueNotUnique THEN GOTO DeleteFirst]; EXITS DeleteFirst => { LoganBerry.DeleteEntry[db: db, key: key, value: GetAttr[entry, key]]; LoganBerry.WriteEntry[ db: db, entry: entry]; }; }; GetAttr: PROC[entry: LoganBerry.Entry, key: LoganBerry.AttributeType] RETURNS[value: LoganBerry.AttributeValue_NIL] = { FOR ls: LoganBerry.Entry _ entry, ls.rest WHILE ls # NIL DO IF ls.first.type = key THEN RETURN[ls.first.value]; ENDLOOP; }; PostLBError: PROC[ ec: LoganBerry.ErrorCode, explanation: Rope.ROPE, reopening: BOOL, dbName: Rope.ROPE ] RETURNS[retry: BOOL_FALSE] = { SELECT ec FROM $CantOpenSchema, $CantOpenLog, $CantOpenIndex=> Error[$dbNotFound, IO.PutFR["\n%g: %g", rope[dbName], rope[explanation]]]; $BadSchema, $BadLogEntry, $BadIndex, $LogReadOnly => Error[$invalid, IO.PutFR["%g: %g", rope[dbName], rope[explanation]]]; $DBNotAvailable => Error[$notAvailable, IO.PutFR["\n%g: %g", rope[dbName], rope[explanation]]]; $DBClosed, $BadDBHandle => IF reopening THEN { Error[$notAvailable, IO.PutFR["\n%g: %g", rope[dbName], rope[explanation]]] } ELSE RETURN[retry: TRUE]; $ValueNotUnique => Error[$voiceIDNotUnique, IO.PutFR["\n%g: %g", rope[dbName], rope[explanation]]]; $NoIndex => Error[$refIDNotFound, IO.PutFR["\n%g: %g", rope[dbName], rope[explanation]]]; $NoPrimaryKey => Error[$clientError, IO.PutFR["\n%g: %g", rope[dbName], rope[explanation]]] ENDCASE; -- All others expected to propagate as uncaught signals }; PostRPCError: PROC[ handle: VoiceDBHandle, why: RPC.CallFailure, reopening: BOOL ] RETURNS[retry: BOOL] = { SELECT why FROM $timeout, $busy => Error[$communications, IO.PutFR["\nNo response from voice message database %g", rope[handle.instance]]]; $runtimeProtocol, $stubProtocol => Error[$invalid, "\nProtocol violation in communications with voice message database"]; $unbound => IF ~reopening THEN { -- Force reopening of all databases handle.tunesDB _ handle.tuneRefDB _ RefID.nullID; handle.imported _ FALSE; RETURN[TRUE]; } ELSE Error[$invalid, IO.PutFR["\nCan't bind to voice message database instance %g", rope[handle.instance]]]; ENDCASE=>ERROR; }; OpenLB: PROC[handle: VoiceDBHandle, dbName: Rope.ROPE] RETURNS [db: LoganBerry.OpenDB] = { IF ~handle.imported THEN ImportLoganberry[handle]; db _ LoganBerry.Open[dbName: dbName]; }; ImportLoganberry: PROC[handle: VoiceDBHandle] = { ENABLE { RPC.ImportFailed => Error[$NotFound, IO.PutFR["Could not import Loganberry from %g", rope[handle.instance]]]; }; TRUSTED { LoganBerryRpcControl.UnimportInterface[!LupineRuntime.BindingError => CONTINUE];}; LoganBerryRpcControl.ImportInterface[["Loganberry.Lark", handle.instance]]; handle.tunesDB _ handle.tuneRefDB _ RefID.nullID; handle.imported _ TRUE; }; }. 8VoiceDBImpl.mesa Copyright c 1985, 1986 by Xerox Corporation. All rights reserved. Swinehart, May 19, 1986 9:21:00 am PDT Definitions Concrete implementation of VoiceDB.VoiceDBHandle Interface procedures { openEc _ $communications; expl _ IO.PutFR["No response from %g", rope[instance]]; GOTO ReturnAnyway; }; VoiceDB errors raised in called routines propagate to client  This version of VoiceDBImpl only knows how to open remote instances. We get into binder fun and games to do the local/remote thing.  Error[$communications, IO.PutFR["No response from %g", rope[handle.instance]]]; RPC.CallFailed => Error[$communications, IO.PutFR["No response from %g", rope[handle.instance]]]; VoiceDB errors raised in called routines also propagate to client TRUE means change value associated with (secondary) key, FALSE means add new value unless this value already exists. RPC.CallFailed => Error[$communications, IO.PutFR["No response from %g", rope[handle.instance]]]; VoiceDB errors raised in called routines also propagate to client RPC.CallFailed => Error[$communications, IO.PutFR["No response from %g", rope[handle.instance]]]; VoiceDB errors raised in called routines also propagate to client All errors managed higher-up Swinehart, May 19, 1986 9:20:57 am PDT Cedar 6.1 changes to: DIRECTORY, VoiceDBImpl, Create, Retain Κ Λ˜šœ™Icodešœ Οmœ7™BK™&—J˜šΟk ˜ Jšœ žœ ˜J˜Jšžœ˜Jšœ žœn˜~Jšœžœ(˜BJšœžœ˜%Jšœžœ ˜Jšœžœžœ˜#Jšžœžœ+˜4Jšœžœ/˜;Jšœ˜Jšœ!˜!Jšœ˜J˜—šœ žœž˜Jšžœžœ9žœ žœ ˜zJšžœžœ˜—J˜™ J™Jšœžœžœ˜+šœžœžœžœ˜(Kšœ0™0Kšœ(˜(Kšœ*˜*Kšœžœ˜Kšœžœ˜Kšœž˜Kšœ žœž˜K˜K˜—J˜/J˜—™J˜Jš œžœžœ*žœžœžœ˜NJ˜šΟnœžœžœ$žœ˜;Jšžœžœžœ žœ˜Yšžœ˜Jšžœ,žœ˜5šœ™Jšœ™Jšœžœ.™8Jšžœ™Jšœ™—Jšœ,žœ˜AJšœ4žœ ˜BJ™