PageBox |
|
|
|
|
|
Presentation | User guide | Token API | Active naming | Extensions | Implementation | Token implementation |
Token API and Active Naming Implementation
This document presents the implementation of the Token API, measurement facility and Active Naming in PageBox for Java.
Programmers who want:
To participate to the PageBox development
To adapt or extend PageBox for their needs
PageBox implements a token ring using the PageBox Web services:
DeployIF for the PageBox agent
RepoQueryIF for the Repository
The implementation uses the mechanism presented on the Token API page. It is designed as a set of components with very few interactions with the rest of PageBox, which means that
It can be tested and updated almost independently of the rest of PageBox
If it is not used it has a marginal impact on PageBox performance and footprint
The DeployIF interface extends a TokenIF interface defined like this:
interface TokenIF { DeployIF.Status frameSend(TokenFrame frame) throws RemoteException; } |
This TokenIF interface defines a frameSend method that sends a frame to the next PageBox on the ring or to the Repository when the PageBox is the last PageBox on the ring. This method takes one parameter, which is the frame to send.
The frame is an object of TokenFrame class:
class TokenFrame implements Serializable { String repUrl; int nb; LinkedList adjacencyList; LinkedList msgList; /** Methods */ } |
The TokenFrame class implements methods that we describe later. Here we only present its data, which are:
repUrl: the URL of the RepoQueryIF Web service of the Repository
nb: the frame number
adjacencyList: a list of PageBoxInfo objects describing the PageBoxes on the ring
msgList: the list of the message sets carried by the frame. A message set contains messages with the same origin, destination and Web archive and is stored in a TokenMsg object.
The order of the PageBoxes in the adjacency list is the order of the PageBoxes on the ring:
The PageBoxInfo class is defined like this:
class PageBoxInfo implements Serializable { String url; String user; String password; } |
Where:
url is the URL of the DeployIF Web service of the PageBox
user is the user account to use to call the DeployIF Web service of the PageBox
password is the password to use to call the DeployIF Web service of the PageBox
The TokenMsg class is defined like this:
class TokenMsg implements Serializable { String origin; String target; String archive; HashMap msgs; /** Methods */ } |
The TokenMsg class implements methods that we describe later. Here we only present its data, which are:
origin: URL of the Deploy Web service of the origin PageBox
target: URL of the Deploy Web service of the target PageBox, null in case of broadcast
archive: name of the Web archive
msgs: map whose key is the correlation ID and value is a Message object
The Message class is defined like this:
class Message implements Serializable { String type; byte[] data; HashMap codes = null; } |
Where:
type is the message type setup by the Web archive in its send request
data is the message itself serialized by TokenSerializer
codes is a response map whose key is the URL of the Deploy Web service of the answering PageBox and value is the Web application answer
The DeployIF Web service is implemented in the DeployImpl class. DeployImpl extends a TokenImpl class that implements the TokenIF methods.
The RepoQueryIF interface extends a RepoTokenIF interface defined like this:
interface RepoTokenIF extends PageBoxLib.TokenIF { void tokenRegister(String subscriber) throws RemoteException; void tokenUnregister(String subscriber) throws RemoteException; } |
This RepoTokenIF interface extends the TokenIF interface described above and defines two additional methods:
tokenRegister: adds a new PageBox to the ring. This method takes one parameter, which is the URL of the DeployIF Web service of the added PageBox.
tokenUnregister: removes a PageBox from the ring. This method takes one parameter, which is the URL of the DeployIF Web service of the added PageBox.
In this case the TokenIF method, frameSend, sends a frame to the first PageBox on the ring.
The RepoQueryIF Web service is implemented in the RepoQueryImpl class. RepoQueryImpl extends a RepoTokenImpl class that implements the RepoTokenIF methods.
As described in the token API user guide PageBoxAPI implements two methods:
registerCallback to register a TokenCallbackIF object
unregisterCallback to un-register a TokenCallbackIF object
The actual implementation of PageBoxAPI is in the APIImpl class. This class contains a member of type TokenCallbackImpl. PageBoxAPI.registerCallback calls the registerCallback method of this object and PageBoxAPI.unregisterCallback calls the unregisterCallback method of this object.
These classes are used on the Repository as well as on the PageBox agent. These classes are:
TokenFrame
TokenMsg
Message
TokenSerializer
TokenImpl
TokenCallbackImpl
RepoTokenImpl
TokenCallbackImpl has two members of interest:
callbacks whose key is the URL of the RepoQuery Web service of a Repository and value is a RepositoryReg object
msgList: a list of TokenMsg objects queued before the frame reception
RepositoryReg is a class embedded in TokenCallbackImpl that has two functions:
To store a callbacks map whose key is the archive name and value is a TokenCallbackIF object provided by the Web application stored in the archive
To implement the invocation of the tokenRegister and tokenUnregister methods of a Repository RepoQuery Web service. Its register method calls tokenRegister and its unregister method calls tokenUnregister.
TokenCallbackImpl also imbeds a Sender class that implements the TokenSendIF. Its send method adds the message passed in parameter to the msgs map of the proper TokenMsg. If this TokenMsg doesn’t exist send creates a new TokenMsg and adds it to msgList.
This method gets called when a Web archive calls the homonymous method of PageBoxAPI. However the registerCallback method of TokenCallbackImpl has a second parameter, the name of the Web archive. This parameter was set up by PageBoxAPI.
If a RepositoryReg instance doesn’t exist yet for the Repository where the Web archive was downloaded in the callbacks map registerCallback:
Creates a new RepositoryReg
Calls its register method to register the PageBox on this Repository ring
Otherwise registerCallback retrieves the RepositoryReg instance from the callbacks map.
Then registerCallback sets the TokenCallbackIF object of the archive in RepositoryReg.callbacks and creates a Sender object.
This method gets called when a Web archive calls the homonymous method of PageBoxAPI. However the unregisterCallback method of TokenCallbackImpl has a parameter, the name of the Web archive. This parameter was set up by PageBoxAPI.
unregisterCallback removes the archive from the RepositoryReg.callbacks map. If then RepositoryReg.callbacks is empty (if there is not anymore Web applications handling messages on this PageBox) unregisterCallback calls the unregister method of RepositoryReg to remove the PageBox from the Repository ring.
processFrame is called by TokenImpl to handle frames received by the PageBox.
processFrame enumerates the callbacks map of the RepositoryReg instance that manages the frame Repository. For each archive (Web application) found processFrame retrieves its TokenCallbackIF instance, calls the processResponse and processMessage methods of the frame and the poll method of the TokenCallbackIF instance.
Then processFrame calls a cleanup method of the frame to remove messages that came back to their issuer and adds messages in msgList to the frame.
TokenImpl implements the TokenIF method of the DeployIF Web service, frameSend.
Steps:
The previous PageBox on the ring calls the frameSend method of the DeployIF Web service
The frameSend method of TokenImpl calls the processFrame method of TokenCallbackImpl to handle message whose target was this PageBox and to add messages sent by this PageBox
The frameSend method queues the updated frame to the TokenImpl thread (TokenThread class)
The run method of the TokenImpl thread calls the frameForward method to send the updated frame to the next PageBox on the ring
The context of TokenImpl is stored in a TokenImpl.Context object defined like this:
class Context { TokenThread tt = null; TokenFrame lastFrame = null; } |
Where
tt is an instance of a TokenThread class responsible for sending frames to the next station on the ring
lastFrame is the last frame sent by TokenImpl
frameSend calls the processFrame method of TokenCallbackImpl to handle message whose target was this PageBox and to add messages sent by this PageBox, queues the updated frame and notifies the TokenThread instance that there is a new frame to process.
TokenThread extends Thread. In this thread TokenThread dequeues and forwards the frames queued by the frameSend method to the next PageBox on the ring.
The run method is the main method of the TokenThread thread. It waits for a notification or for a timeout. If it was notified by frameSend it calls the frameForward method to send the updated frame to the next PageBox on the ring. In case of timeout run checks if the repository is alive by sending it a special "ping" frame. If the repository is dead run calls a generateToken method.
generateToken wait for a random amount of time to:
call the processFrame method of TokenCallbackImpl to add messages sent by this PageBox;
call to frameForward method to forward this updated frame to the next station on the ring.
frameForward finds the next PageBox in the adjacency list and calls its DeployIF Web service to send the frame. In case of failure it calls the DeployIF Web service of the next PageBox up to the time it succeeds (finds a running PageBox). When there is no more PageBox frameForward calls the RepoQueryIF Web service of the Repository.
In both the run and the frameForward methods failed PageBoxes are removed from the adjacency list and the RepoQueryIF.tokenUnregister method of the Repository is called to remove the failed PageBoxes from the ring.
TokenFrame implements trace methods and three methods called by the processFrame method of TokenCallbackImpl:
processMessage
processResponse
cleanup
processMessage retrieves from the frame msgList the TokenMsg objects whose archive is the archive passed in parameter and target is this PageBox or null (broadcast).
processMessage enumerates the Message instances in the msgs map of these TokenMsg objects. For each Message processMessage invokes the call method of the Web application’s TokenCallbackIF object.
processResponse retrieves from the frame msgList the TokenMsg objects whose archive is the archive passed in parameter and origin is this PageBox.
processResponse enumerates the Message instances in the msgs map of these TokenMsg objects. For each Message processResponse invokes the response method of the Web application’s TokenCallbackIF object.
There are two versions of the cleanup method:
A version called by TokenCallbackImpl.processFrame on PageBox agents with a parameter that removes TokenMsg objects that came back to their issuer
A version called by RepoTokenImpl.frameSend on Repository without parameter that removes TokenMsg objects whose origin or destination is no longer in the adjacency list
RepoTokenImpl is the Repository equivalent of TokenImpl.
RepoTokenImpl implements the RepoTokenIF methods of the RepoQueryIF Web service. Because RepoTokenIF extends TokenIF RepoTokenImpl implements frameSend. Therefore like TokenImpl RepoTokenImpl and creates a separate RepoTokenThread thread that forwards the updated frame to the first PageBox on the ring. RepoTokenImpl also implements two methods of RepoTokenIF, tokenRegister and tokenUnregister.
The context of RepoTokenImpl is stored in a RepoTokenImpl.Context object defined like this:
class Context { TokenFrame master = null; TokenFrame lastFrame = null; RepoTokenThread rtt = null; } |
Where
master is a TokenFrame object sent when the Repository issues a new token. RepoTokenImpl also uses master to keep an up-to-date adjacency list.
rtt is a RepoTokenThread object responsible for sending frames to the first station on the ring
lastFrame is the last frame sent by RepoTokenImpl.
If master is null (if a first PageBox joins the ring) tokenRegister instantiates a new TokenFrame with the PageBox in the adjacency list and queues this frame to the RepoTokenImpl thread for sending the frame. Otherwise tokenRegister adds the PageBox to the master adjacency list.
tokenUnregister removes the PageBox from the master’s adjacency list. If this PageBox was the last PageBox on the ring tokenUnregister ends the RepoTokenImpl thread and unreferences the master.
frameSend
Replaces the frame adjacency list by the master adjacency list
Calls the cleanup method of TokenFrame to remove messages whose origin or destination is no longer on the ring
Queues the updated frame to the RepoTokenThread thread for sending the frame
RepoTokenThread extends Thread. In this thread RepoTokenThread dequeues and forwards the frames queued by the frameSend method to the first PageBox on the ring.
The run method is the main method of the RepoTokenThread thread. It waits for a notification or for a timeout. If it was notified by frameSend or tokenRegister run sends the frame to the first running PageBox and calls TokenUnregister to remove the non-running PageBoxes before the first running PageBox from the ring.
In case of timeout run issues a new token (sends a new frame). It addresses the case where the token was lost but also ensures that there are enough tokens on the ring to handle messages fast enough.
When it receives the token earlier than half timeout + the time when it sent this token on the ring, run waits in order for two subsequent frame sends to be separated by half timeout.
TokenSerializer addresses a class resolution problem. The Token API allows sending and receiving objects of any Serializable type known by the Web application class loader. TokenFrame that handles messages and responses in its processMessage and processResponse methods is loaded by a different class loader, the PageBoxLib class loader.
To serialize and un-serialize messages the processMessage and processResponse methods load the TokenSerializer class with the Web application class loader, create a TokenSerializer object and use the reflection to invoke the processMessage and processResponse methods of this TokenSerializer object.
TokenSerializer implements three methods for Token handling:
processMessage that un-serializes the message object, invokes the call method of the Web application callback and serializes the call response
getMessage that returns the error message in case of processMessage failure
processResponse that un-serializes the response object
TokenSerializer implements three other methods for Active Naming:
mustActive that unserializes the imperative objects of remote Web applications and use the equals method to check if they equal the imperative object passed as parameter
niceifActive that unserializes the nice if match objects of remote Web applications and use the compareTo method to measure their distance to the nice if match object passed as parameter
serializeActive that serializes objects used for imperative and nice if matching
The measurement facility measures:
The CPU use of a PageBox
The memory use of a PageBox
The network performance
of a PageBox host.
This facility is modular and extensible to allow supporting new Operating Systems or making better measurements. We describe in the Porting guide how to extend the facility. Collected data are stored in a ResourceUsage class that extends a MemUsage class. Operating system dependent probes are responsible for making measurements. PageBox 0.0.13 embeds a probe for Linux, called LinuxUsageProbe and a probe for Windows NT, 2000 and XP, called WindowsUsageProbe. Probes are instantiated by a UsageFactory class and implement a UsageIF interface defined like this:
interface UsageIF { ResourceUsage collect(); } |
ResourceUsage is just a placeholder for CPU-related data normally set by the probes.
MemUsage is a placeholder for memory and network related data but it also implements an init method.
This init method takes one parameter, netTime, which is the time spent to send a frame to the next station on the ring and sets the MemUsage members in the following way:
Member | Content |
---|---|
netTime | netTime parameter |
free | Runtime.getRuntime().freeMemory() |
total | Runtime.getRuntime().totalMemory() |
max | Runtime.getRuntime().maxMemory() |
APIImpl.getResourceUsage calls the init method after it has called the collect method of the probe.
UsageFactory has a getUsageProbe static method that instantiates the appropriate probe depending on the Operating System. UsageFactory.getUsageProbe is called by APIImpl.getResourceUsage.
LinuxUsageProbe implements the collect method of the UsageIF interface. Its collect method reads the /proc/stat file and parses its cpu line.
WindowsUsageProbe implements the collect method of the UsageIF interface. Its collect method loads a WindowsCPU dll and calls its getCPU native method. getCPU calls a GetCpuUsage usage method implemented in a GetCpuUsage.cpp file. We didn’t develop this code. We found it on http://www.codeproject.com/system/cpuusage.asp where it is credited to Dudi Avramov. We only added the JNI stuff and converted the exe into a dll.
PageBox implements Active Naming using the Token API and the measurement facility.
The implementation uses the mechanism presented on the Active Naming page. It has no interaction with the rest of PageBox except the Token implementation.
As described in the Active Naming user guide the PageBoxAPI implements seven methods:
ActiveNamingLogon to log on Active Naming within a client role
ActiveNamingLogoff to log off Active Naming within a client role
getEntries to enumerate the logical names of the entry points declared on the ring
getCandidate to get the URL (Web service, page) of the most appropriate service instance
addEntry to declare an entry point and service instance
removeEntry to remove an entry point
clearEntries to clear the entry points of a Web application instance
The actual implementation of PageBoxAPI is in the APIImpl class. This class contains a member of type ActiveNaming. PageBoxAPI.ActiveNamingLogon calls the logon method of this object, PageBoxAPI.ActiveNamingLogoff calls the logoff method of this object, PageBoxAPI.getEntries calls the getEntries method of this object, PageBoxAPI.getCandidate calls the getCandidate method of this object, PageBoxAPI.addEntry calls the addEntry method of this object, PageBoxAPI.removeEntry calls the removeEntry method of this object and PageBoxAPI.clearEntries calls the clearEntries method of this object.
APIImpl also contains a getResourceUsage that uses the measurement facility to return a populated ResourceUsage object that describes resource usage on a PageBox host.
Active Naming is implemented in the Active Naming class.
Four other classes store data:
ActiveEntry
ActiveMessage
MatchingEntry
ActiveResult
Active Naming also contains an ActiveComparable interface.
ActiveEntry is defined like this:
class ActiveEntry implements Serializable { String url; byte[] must; byte[] niceif; transient public int distance; } |
ActiveEntry describes a service instance. url is the page or Web service URL. must is the imperative matching object serialized by TokenSerializer. niceif is the nice if match object serialized by TokenSerializer.
ActiveEntry objects are inserted in ActiveMessage objects, which are themselves inserted in Token ring frames. Therefore ActiveEntry implements Serializable.
ActiveMessage is defined like this:
class ActiveMessage implements Serializable { public HashMap entries = null; public ResourceUsage usage = null; } |
ActiveMessage is the token ring message used to send Active Naming data. Because ActiveMessage objects are inserted in Token ring frames ActiveMessage implements Serializable. entries is a map whose keys are entry names and values are ActiveEntry objects.
MatchingEntry is defined like this:
class MatchingEntry { ActiveEntry ae; ResourceUsage ru; int distance; } |
distance is the distance between the service instance and a niceif object passed by the getCandidate method. This distance is setup by the niceifActive method of TokenSerializer.
MatchingEntry is used by ActiveNaming.getCandidate to communicate with TokenSerializer.
ActiveResult is used by the mustActive and niceifActive methods of TokenSerializer to return their response. ActiveResult is defined like this:
class ActiveResult { public int max = -1; public ArrayList entries = new ArrayList(); public String msg = "no entry found"; } |
Where
max contains the maximum distance between an service instance and a niceif object passed by the getCandidate method when ActiveResult is returned by niceifActive. max contains 0 when ActiveResult is returned by mustActive. max contains -1 in case of error in both mustActive and niceifActive
entries contains MatchingEntry objects
msg contains an error message valid when max == -1
ActiveComparable is the interface that nice if match object must implement. These objects must be serializable because nice if match objects must be serialized into ActiveEntry.niceif and implement a compareTo method to measure the distance between two nice if match objects. Therefore ActiveComparable is defined like this:
interface ActiveComparable extends Serializable { int compareTo(Object o, ResourceUsage usage); } |
ActiveNaming implements:
The TokenCallbackIF interface, the methods of interest for this discussion being call and send
The methods called by PageBoxAPI, logon, logoff, getEntries, getCandidate, addEntry, removeEntry and clearEntries.
ActiveNaming has three members of interest:
localEntries that contains service instances declared by the Web application acting as a server. localEntries is a HashMap whose key is the logical name of an entry point and value is an ActiveEntry object.
foreignEntries that contains service instances declared by the Web application clones deployed from the same repository. foreignEntries is a HashMap whose key is the URL of the DeployIf Web service of a deploying PageBox and value is another HashMap whose key is the logical name of an entry point and value is an ActiveMessage object.
isRegistered, a Boolean set to true by logon and to false by logoff
ActiveNaming may register on the ring for two reasons:
When the Web application acts as a client because logon has been called
When the Web application acts as a server because it has declared service instances. In this case localEntries is not empty.
Therefore logon checks if localEntries is empty. When it is the case logon registers its ActiveNaming instance as a Token API callback with the registerCallback method of the PageBox API.
The Token API allows only one callback per archive. To allow using the Active Naming and the Token API at the same time Active Naming registers a pseudo archive name made of the actual archive suffixed by ".an".
logoff unregisters from the ring only if localEntries is empty (if the Web application has no more service instances to advertise).
getEntries enumerates foreignEntries to build a list of logical names of entry points.
getCandidate is called with three parameters:
name: logical name of an entry point
must: imperative matching object
niceif: nice if match object
getCandidate:
Enumerates foreignEntries to build a first list of services instances whose logical name of entry point is "name"
If "must" is not null, creates a TokenSerializer instance with the "must" class loader to compare the "must" parameter to the must member of service instances in the first list and build a second list with only service instances whose must member matches the "parameter"
If "niceif" is not null, creates a TokenSerializer instance with the "niceif" class loader to measure the distance between the "niceif" member of service instances in the second list and build a third list of service instances
Build a fourth list with each service instance represented (maximum distance - instance distance to "niceif" + 2) times when the service instance had a non-null niceif member and only once otherwise
Return a randomly chosen service instance in the fourth list
addEntry adds a new ActiveEntry object to the localEntries map. If the localEntries was empty before and if logon had not been called (isRegistered == false) addEntry registers its ActiveNaming instance as a Token API callback with the registerCallback method of the PageBox API.
To populate the ActiveEntry object addEntry creates a TokenSerializer instance with the "must" class loader to serialize the "must" parameter and creates a TokenSerializer instance with the "niceif" class loader to serialize the "niceif" parameter.
removeEntry removes the service instance identified by the logical name of entry point from the localEntries map. If then localEntries is empty and if logon had not been called (isRegistered == false) removeEntry un-registers its ActiveNaming instance with the unregisterCallback method of the PageBox API.
clearEntries clears the localEntries map. Then if logon had not been called (isRegistered == false) clearEntries un-registers its ActiveNaming instance with the unregisterCallback method of the PageBox API.
From each remote instance call receives an ActiveMessage and adds this object to the foreignEntries map.
poll build an ActiveMessage with the localEntries map and the ResourceUsage returned by APIImpl.getResourceUsage and sends this ActiveMessage to remote instances.
Contact:support@pagebox.net
©2002-2004 Alexis Grandemange.
Last modified
.