DIRECTORY
Arpa USING [ Address, nullAddress ],
ArpaUDP USING [ Port ],
Basics USING [ Card16FromH, LowHalf, HFromCard16 ],
CardTab USING [ Ref, Create, Fetch, Store ],
Convert USING [ ArpaAddressFromRope ],
IO,
RefID USING [ ID ],
RefTab USING [ Create, Delete, Fetch, Ref, Store ],
Rope USING [ FromRefText, ROPE ],
RPC USING [ Conversation, GenerateConversation ],
RuntimeError USING [ UNCAUGHT ],
SunPMap USING [ ipProtocolUDP, udpPort ],
SunPMapLocal USING [ SetLocal, ipProtocolUDP ],
SunPMapClient USING [ GetPort ],
SunRPC USING [ Destroy, DestroyServer, Error, Handle, ReleaseReply, SendCallAndReceiveReply, Server, ServerProc, StartCall ],
SunRPCOnUDP USING [ Create, CreateServer, GetServerPort, nullPort, SetRemote ],
SunRPCAuth USING [ Initiate ],
SunYPAgent USING [Error, Handle, Match, ObtainHandle, ReleaseHandle, Tokenize],
SRPCCalls USING [ Conversation, ReportProc, SHandle, SunRPCHandleBody, TimeoutEnable ],
VoiceUtils USING [ SunProtocol, SunAddrFromRope ];
Handle Management
NewRPCConversation creates a Conversation, an SHandle, and a dummy RPC Handle, but does not actually import the interface. Instead, the interface is imported any time a call is made when the SHandle indicates that the remote server is disconnected. In addition, whenever a call fails due to timeout or other potentially correctable problems, an attempt is made to re-bind to the remote service and retry the call before giving up. But only one attempt is made.
Conversation: TYPE ~ SRPCCalls.Conversation;
SHandle: TYPE ~ SRPCCalls.SHandle;
SHandleBody: TYPE ~ SRPCCalls.SunRPCHandleBody;
TimeoutEnable: TYPE ~ SRPCCalls.TimeoutEnable;
ID: TYPE ~ RefID.ID;
conversationToSHandleMap: RefTab.Ref ¬ RefTab.Create[];
handleToSHandleMap: RefTab.Ref ¬ RefTab.Create[];
NewRPCConversation:
PUBLIC
PROC [
serverName: Rope.
ROPE, rpcProgram:
CARD, rpcVersion:
CARD,
clientData: REF, reportProc: SRPCCalls.ReportProc ¬ NIL, timeoutEnable: TimeoutEnable, timeoutInMs: INT, retries: INT] RETURNS [conversation: Conversation] ~ {
sHandle: SHandle;
conversation ¬ RPC.GenerateConversation[];
sHandle ¬ NEW[SHandleBody ¬ [serverName: serverName, rpcProgram: rpcProgram, rpcVersion: rpcVersion, timeoutEnable: timeoutEnable, timeoutInMs: timeoutInMs, retries: retries, clientData: clientData, conversation: conversation, reportProc: reportProc, handle: SunRPCOnUDP.Create[]]];
[]¬conversationToSHandleMap.Store[conversation, sHandle];
[]¬handleToSHandleMap.Store[sHandle.handle, sHandle];
-- Dummy handle until really imported
};
ReleaseConversation:
PUBLIC
PROC [conversation: Conversation] ~ {
sHandle: SHandle;
IF conversation = NIL THEN RETURN;
sHandle ¬ GetSHandle[conversation];
IF sHandle = NIL THEN RETURN;
[]¬conversationToSHandleMap.Delete[conversation];
IF sHandle.handle#
NIL
THEN {
[]¬handleToSHandleMap.Delete[sHandle.handle];
SunRPC.Destroy[sHandle.handle];
};
sHandle.connected ¬ FALSE;
sHandle.conversation ¬ NIL; -- Break any
sHandle.handle ¬ NIL; -- loops
sHandle.clientData ¬ NIL;
};
GetSHandle:
PUBLIC PROC[conversation: Conversation]
RETURNS [sHandle: SHandle] ~ {
RETURN[NARROW[conversationToSHandleMap.Fetch[conversation].val]];
};
SHandleFromHandle:
PROC[handle: Handle]
RETURNS [sHandle: SHandle] ~ {
RETURN[NARROW[handleToSHandleMap.Fetch[handle].val]];
};
ImportInterface:
INTERNAL
PROC[sHandle: SHandle]
RETURNS[errCode: ATOM ¬ NIL, expl: ROPE ¬ NIL] ~ {
ENABLE {
SunRPC.Error => {
errCode ¬ code;
SELECT code
FROM
$unreachable, $timeout => NULL; ENDCASE => sHandle.enabled ¬ FALSE;
CONTINUE;
};
RuntimeError.
UNCAUGHT => {
errCode ¬ $UnknownError; sHandle.enabled ¬ FALSE; CONTINUE; };
};
server: Rope.ROPE ¬ sHandle.serverName;
addr: Arpa.Address ¬ Arpa.nullAddress;
handle, oldHandle: Handle;
sHandle.connected ¬ FALSE;
oldHandle ¬ sHandle.handle;
IF VoiceUtils.SunProtocol[server] THEN {
uPort: ArpaUDP.Port;
[addr, uPort] ¬ VoiceUtils.SunAddrFromRope[server];
handle ¬ SunRPCOnUDP.Create[addr, uPort];
}
ELSE IF server#
NIL
THEN {
port: CARD;
addr ¬ SunYPNameToAddress[server];
handle ¬ SunRPCOnUDP.Create[addr, Basics.HFromCard16[SunPMap.udpPort]];
port ¬ SunPMapClient.GetPort[handle, SunRPCAuth.Initiate[], sHandle.rpcProgram, sHandle.rpcVersion, SunPMap.ipProtocolUDP ! SunRPC.Error =>
{
errCode ¬ code;
expl ¬ IO.PutFR1["Can't contact remote server: %g.", IO.rope[server]];}];
IF errCode#NIL THEN RETURN;
IF port = 0
THEN
RETURN [$CantConnect, IO.PutFR1["ThParty not exported on server: %g.", IO.rope[server]]];
handle ¬
SunRPCOnUDP.SetRemote[handle, addr, Basics.HFromCard16[Basics.LowHalf[port]]];
};
IF addr = Arpa.nullAddress
THEN
RETURN[$CantOpenSchema, IO.PutFR1["Unknown server: %g.", IO.rope[server]]];
[]¬handleToSHandleMap.Store[handle, sHandle];
sHandle.handle ¬ handle;
IF oldHandle#
NIL
THEN {
[]¬handleToSHandleMap.Delete[oldHandle];
SunRPC.Destroy[oldHandle];
};
sHandle.connected ¬ TRUE;
};
ypMap: ROPE ¬ "hosts.byname";
SunYPNameToAddress:
PROC [name:
ROPE]
RETURNS [addr: Arpa.Address ¬ Arpa.nullAddress] ~ {
ENABLE SunYPAgent.Error => CONTINUE;
mapEntry: REF TEXT ¬ NIL;
h: SunYPAgent.Handle ¬ SunYPAgent.ObtainHandle[]; -- use default domain
mapEntry ¬ SunYPAgent.Match[h, ypMap, name];
SunYPAgent.ReleaseHandle[h];
IF mapEntry #
NIL
THEN {
map entry is something like "13.0.208.72 baobab baobab.parc.xerox.com"
addrText: REF TEXT ¬ SunYPAgent.Tokenize[mapEntry][0]; -- address is at front of mapEntry
addr ¬ Convert.ArpaAddressFromRope[Rope.FromRefText[addrText]];
};
};
ExportSunRPCInterface:
PUBLIC
PROC [
previousPort: CARD¬0, server: SunRPC.ServerProc, rpcPgm: CARD, rpcPgmVersion: CARD]
RETURNS [uniquePort: CARD] ~ {
ENABLE SunRPC.Error => Error[code, "export failed"];
ok: BOOL; -- Do something with this!!
theServer: SunRPC.Server ¬ GetServer[previousPort]; -- in case server already running
IF theServer#NIL THEN SunRPC.DestroyServer[theServer]; -- remove old server
theServer ¬ SunRPCOnUDP.CreateServer[pgm~rpcPgm, serverProc~server, version~rpcPgmVersion, port: SunRPCOnUDP.nullPort ];
uniquePort ¬ Basics.Card16FromH[SunRPCOnUDP.GetServerPort[theServer]];
ok ¬ SunPMapLocal.SetLocal[program~rpcPgm, version~rpcPgmVersion, protocol~SunPMapLocal.ipProtocolUDP, port~uniquePort];
[] ¬ sunServerTable.Store[key: uniquePort, val: NEW[SunRPC.Server ¬ theServer] ];
};
A table is kept to map ports to servers, so that an old version of a server can be removed before a new one is exported. The SunPMap package does a similar job, but it distinguishes only on the basis of program and version, so doesn't support multiple instances of an interface exported from the same machine.
sunServerTable: CardTab.Ref ¬ CardTab.Create[];
WrapOpaqueType:
TYPE =
REF SunRPC.Server;
Can't NARROW or LOOPHOLE to an opaque type
GetServer:
PROC [port:
CARD]
RETURNS [SunRPC.Server] =
INLINE {
wrapper: WrapOpaqueType ¬ NARROW[CardTab.Fetch[x: sunServerTable, key: port].val];
RETURN[IF wrapper=NIL THEN NIL ELSE wrapper]
};
}.