PageBox for Java: Web application deployment using Java PageBox

for
 PageBox for Java 
 API 
 Demo 
 Background 
Presentation Install User guide Developer guide Programming Port Repository PageBox Release notes

Repository of PageBox for Java

Foreword

Objective

This document presents the implementation of the PageBox repository.

To present the design we use a representation that loosely follows UML.

Audience

Programmers who want:

  1. To participate to the PageBox development

  2. To adapt or extend PageBox for their needs

  3. To understand the PageBox design

This documentation assumes some knowledge of UML, Java and J2EE.

Design choices

  1. Model View Controller (MVC) architecture

  2. Data stored in XML

  3. Archive upload with HTTP POST

Use cases

The Repository interfaces with:

  1. Archive publishers

  2. PageBox subscribers

  3. PageBoxes for deployment and to give the list of sibling PageBoxes

Publisher

The publisher – maybe the author of an Application – packages a PageBox archive and uploads this archive on a Repository.

The publisher can publish or unpublish a Web archive.

In case of new archive:

  1. The publication triggers the deployment of the archive on the subscribed PageBox

  2. The published archive becomes available for selection by archive subscribers

In case of archive update:

  1. The difference between the old and the new version of the archive is computed in jardiff format

  2. The jardiff is deployed on all PageBoxes where the old version was installed

The unpublication triggers the undeployment of the archive on every PageBox where it was deployed.

Subscriber

The subscriber registers one or more Pageboxes as agents interested by all (normal subscription) or some (archive subscription) archives available on a Repository.

The subscriber can subscribe or unsubscribe a PageBox.

This use case can be divided in two cases:

  1. Subscribe where all archives published on the Repository are deployed

  2. Archive subscribe where the subscriber selects the archives to be deployed

A normal subscription triggers the deployment of all archives published on the Repository onto the subscribed PageBox.

The selection of an archive for an archive-subscribed pagebox triggers the deployment of this archive onto the archive-subscribed PageBox.

The unselection of an archive for an archive-subscribed pagebox triggers the undeployment of this archive from the archive-subscribed PageBox.

The unsubscription of a PageBox triggers the undeployment of all archives deployed from the Repository.

PageBox

The repository calls a Deploy Web service to deploy and undeploy Web archives on PageBoxes.

When the deployment is triggered by a publication the archive must be deployed on all subscribers. In that case some PageBoxes can act as archive deployment relays. See the Grid API V2 documentation for more information about the algorithm.

In the diagram the Repository must deploy an archive on PageBox1, PageBox2 and PageBox3.

The Repository calls the Deploy Web service of PageBox1.

PageBox1 acts as a Relay and:

  1. Handles the deployment request

  2. Returns the deployment status to the Repository

  3. Queues the deployment request to the Relay thread that calls the Deploy Web service of PageBox3 and calls the RepoQuery Web service of the Repository to return the deployment status of PageBox3

Once it has received the PageBox1 deployment status the Repository calls the Deploy Web service of PageBox2.

Through that mechanism deployments on PageBox2 and PageBox3 happen in parallel.

Publish

Sequence diagrams

Upload

publish.jsp displays a form that displays the archives already published by the Publisher.

The Publisher enters the archive path and triggers an upload.

PublishArchive handles the upload request, stores the uploaded archive and calls the add method of RepoArchs. Then PublishArchive forwards the HTTP request to publish.jsp to display the updated archive list.

Delete/force

publish.jsp displays a form that displays the archives already published by the Publisher.

The Publisher selects an archive to delete.

Publish handles the delete request and calls the delete method of RepoArchs. Then Publish forwards the HTTP request to publish.jsp to display the updated archive list.

The difference between delete and force is that if the undeployment failed the archive is not removed from a subscriber in case of delete whereas it is removed in case of force.

Class diagram

publish.jsp is the view and displays the content of the PublishBean model.

RepoArchs reads archives.xml in its init method and rewrites archives.xml in its save method. RepoArchs updates the PublishBean model.

Publish and PublishArchive are the controller. They interpret the user request and invoke RepoArchs.

RepoArchs

If it doesn’t exist yet, RepoArchs instantiates and initializes a RepoArchs singleton in its public methods, add, delete and getArchives, which are static.

The initialization takes place in the init method. init creates a PublishBean object and restores archives.xml in a archives member variable of this PublishBean instance using the ArchiveHandler SAX handler.

archives is a TreeMap whose key is the archive name and value is an Archive object:

class Archive {

String owner;

String docUrl;

String date;

String size;

Boolean pendingRemove;

boolean runInstall;

boolean deltaUpload;

}

Where:

  • owner is the user ID of the publisher who published the archive

  • docUrl is the URL of the archive documentation

  • date is the date when the archive was uploaded

  • size is the archive size

  • pendingRemove is true if an unpublish was requested but not performed because it wasn’t removed from all subscribed PageBoxes.

  • runInstall is true if the archive should be installed by subscribed PageBoxes

  • deltaUpload is true when the uploaded archive is a jardiff. In that case the Repository never computes a diff. The Repository always deploys the archive with isUpdate=true and delete the archive with keepDir=true

Public static methods invoke private methods of RepoArchs:

Public static method

Private method

add

aadd

delete

adelete

These methods handle the creation of delta jardiff archives (aadd) and the deletion of archives (adelete)

aadd

In case of archive creation aadd moves the uploaded archive from Uploaddir to Downloaddir. Then aadd creates a new Archive object and stores it in the PublishBean’s archives. aadd checks if the uploaded archive is a jardiff with a checkDelta method. If it is the case aadd sets the archive as a delta.

In case of archive update aadd calls a diff method to create a jardiff between the old version of the archive stored in Downloaddir and the new version stored in Uploaddir. Then it replaces the old version by the new version. Next aadd updates the archives map. If the uploaded archive is a jardiff, diff doesn’t create a new jardiff, just replace the old version by the new version and sets the archive as a delta.

In both cases aadd calls the add method of RepoSubs and RepoASubs to deploy the archive on the subscribers and update their archive list.

jardiff is a format defined in the JNLP specification used in products such as Java WebStart.

A jardiff is a Java archive that contains:

  • The members modified or created

  • META-INF/INDEX.JD, which in case of the Repository implementation contains only "remove" entries for members that no longer exist

Note:

Jardiff uploads are handled like archive creations. The publisher cannot mix modes:

When an archive has been created with a full load it must be updated with full loads.

When a delta has been created it must be updated with jardiff.

adelete

adelete calls the delete method of RepoSubs and RepoASubs to undeploy the archive on its subscribed and archive-subscribed PageBoxes.

Then adelete deletes the archive in Downloaddir and its jardiff in Deltadir.

Eventually if the archive was successfully removed from all subscribed PageBoxes adelete removes the archive entry from the PublishBean’s archives. Otherwise adelete set the archive entry in pending remove state.

deletePending

deletePending is called in a SaveSubs method called checkArchive on behalf of retryDeploy in the Retry thread when all PageBox that subscribed the archive have successfully removed

deletePending removes the archive entry from the PublishBean’s archives.

PublishBean

getArchives

PublishBean implements a static getArchives method, which is called by publish.jsp.

getArchives retrieves the RepoArchs instance and then the embedded PublishBean instance.

Next getArchives determines if the user requested a date sort with getParameter("date"): Two buttons are defined on publish.jsp to allow sorting by archive name or by publication date. When the user clicks on the date button a date parameter is set.

Eventually getArchives calls the getArchs method of PublishBean.

getArchs

getArchs returns an HTML string that contains a table row per archive published on the Repository by the same user as the requestor or by any user when the user is in a pagebox-admin role. These rows can be sorted either by archive name or by publication date.

getArchs uses the archives map, which is sorted by archive name. In case of sort by date getArchs populates a temporary TreeMap tm with the content of archives. The tm key is a DateArch object.

The DateArch class embeds the publication date and the archive name. DateArch implements the Comparable interface in a consistent with equals way. To do this DateArchs implements the compareTo method of the Comparable interface and overrides the equals method.

In case of date sort getArchs implements a sort of toggle. The first time the user clicks on date a order boolean is false, which means that archives must be sorted the most recent first. Then getArchs writes a hidden order field in the HTML string. The next time the user clicks on date getArchs retrieves this parameter. Then getArchs sorts the archives the oldest first. The ordering is actually implemented in the compareTo method of DateArchs.

PageBoxList

Principle

The publisher can display the installation log of an archive on all PageBoxes where the archive has been installed. This is a two-step process:

The HTML string returned by the PublishBean’s getArchs method shows archives as links defined like this:

<a href="pageboxlist?arch=archive" target="_blank">archive</a>

The link points on a PageBoxList servlet that lists the PageBoxes where the archive is installed, populates a ListBean object and forwards the request to a pageboxlist.jsp page. This is another example of MVC design where PageBoxList is the controller, ListBean is the model and pageboxlist.jsp is the view.

The HTML string returned by pageboxlist.jsp shows PageBoxes as links defined like this:

<a href="pageboxaudit?arch=archive&pb=pageboxURL">pageboxURL</a>

The link points on a PageBoxAudit servlet that calls the getAudit method of the PageBox’s DeployIF Web service to get the installation log records of the archive and applies an XSL transformation to display the log.

doGet

doGet calls the getSubscribers methods of RepoSubs and RepoASubs to build a map of the subscribers and archive subscribers for the archive given in the arch parameter. Then it instantiates a ListBean object with this map, adds the ListBean object to the request attributes and forwards the request to pageboxlist.jsp.

ListBean

pageboxlist.jsp calls the getPageBoxes method of the ListBean object.

The getPageBoxes method returns a HTML row per subscriber found in the subscriber map.

PageBoxAudit

The doGet method of PageBoxAudit calls the getAudit method of RepoSubs and RepoASubs to get the log records recorded on the PageBox given in parameter for the archive given in parameter. Then doGet applies an XSL transformation to the log stream with the audit-pagebox.xsl stylesheet and returns the transformation result to the user.

Subscribe

Sequence diagram

This sequence diagram shows the subscribe, unsubscribe and force cases.

subscribe.jsp displays a form that displays the PageBoxes already subscribed by the Subscriber.

In case of subscribe:

  1. The subscriber enters the URI of the PageBox’s Deploy Web service, the user and password of the PageBox and triggers a POST to Subscribe.

  2. Subscribe handles the subscription request and calls the subscribe method of RepoSubs.

  3. Subscribe forwards the HTTP request to subscribe.jsp to display the updated PageBox list.

In case of unsubscribe or force:

  1. The subscriber can click on an unsub/force button on the PageBox list or enter the URI of the PageBox’s Deploy Web service. In both cases the subscriber triggers a POST to Subscribe.

  2. Subscribe handles the unsubscription/force request and calls the unsubscribe method of RepoSubs.

  3. Subscribe forwards the HTTP request to subscribe.jsp to display the updated PageBox list.

The difference between unsubscribe and force is that if the undeployment failed the subscribed PageBox state changes to "pending remove" in case of delete whereas the subscribed PageBox is removed in case of force.

Class diagram

subscribe.jsp is the view and displays the content of the SubscribeBean model.

RepoSubs extends SaveSubs that implements methods also used in RepoASubs such as save.

RepoSubs reads subscribers.xml in its init method and rewrites subscribers.xml with the SaveSubs’ save method. RepoSubs updates the SubscribeBean model.

Subscribe is the controller. It interprets the user request and invokes RepoSubs.

The implementation details are the same as for archive subscriber and described later.

Archive subscribe

Sequence diagram

This sequence diagram shows:

  • The archive subscribe, unsubscribe and force cases

  • The archive select and unselect cases

asubscribe.jsp displays a form that displays the PageBoxes already subscribed by the Subscriber.

In case of subscribe:

  1. The subscriber enters the URI of the PageBox’s Deploy Web service, the user and password of the PageBox and triggers a POST to ASubscribe.

  2. ASubscribe handles the subscription request and calls the subscribe method of RepoASubs.

  3. ASubscribe forwards the HTTP request to asubscribe.jsp to display the updated PageBox list.

In case of unsubscribe or force:

  1. The subscriber can click on an unsub/force button on the PageBox list or enter the URI of the PageBox’s Deploy Web service. In both cases the subscriber triggers a POST to ASubscribe.

  2. ASubscribe handles the unsubscription/force request and calls the unsubscribe method of RepoASubs.

  3. ASubscribe forwards the HTTP request to asubscribe.jsp to display the updated PageBox list.

The difference between unsubscribe and force is that if the undeployment failed the subscribed PageBox state changes to "pending remove" in case of delete whereas the subscribed PageBox is removed in case of force.

The subscriber can select a subscribed PageBox to display the list of the archives deployed on this PageBox. The subscriber actually links to aselect.jsp.

  1. aselect.jsp displays all the archives published on the repository with a checkbox checked if the archive is deployed on the PageBox. The subscriber can change the checked archives. Then the subscriber triggers a POST to ASelect.

  2. ASelect calls the update method of RepoASubs to deploy the archives checked now but not before and undeploy the no longer checked archives.

  3. ASelect forwards the HTTP request to aselect.jsp to display the updated archive list

Class diagram

There are two view/controller pairs that share the same model:

  1. asubscribe.jsp (view) / ASubscribe (controller)

  2. aselect.jsp (view) / ASelect (controller)

Views display the SubscribeBean model.

Controllers interpret the user request, invoke RepoASubs and update the SubscribeBean model.

RepoASubs extends SaveSubs that implements methods also used in RepoSubs such as save.

RepoASubs reads asubscribers.xml in its init method and rewrites asubscribers.xml with the SaveSubs’ save method.

Subscribe implementation

RepoQuery

The RepoQuery Web service implements the following interface:

interface RepoQueryIF extends Remote {

public String[] GetSubscribers(String archive, String subscriber)

throws RemoteException;

public void Notify(String archive, UrlStatus[] subscribers)

throws RemoteException;

public void NotifyFix(String subscriber, FixArch[] archives)

throws RemoteException;

}

Where:

  • archive is the archive name

  • subscriber is the URI of the Deploy Web service of the calling PageBox

  • UrlStatus is defined like this:

public class UrlStatus {

public String url;

public int code;

public String msg;

}

url being the URI of the Deploy Web service of a PageBox, code a status code and msg an information message about the archive deployment on this PageBox. code can take a value defined in DeployIF.Status among OK, NOTDEPLOYED, NOTUNDEPLOYED, ARCHPB and PBPB. We describes in the SaveSubs section the use and meaning of these codes.

  • FixArch is defined like this:

public class FixArch {

public String name;

public int status;

}

name being an archive name and status an integer that can take a value defined in DeployIF.Status, OK or ARCHPB.

GetSubscribers returns the URIs of the Deploy Web services whose PageBoxes have deployed the archive.

Notify is called by PageBox relays to inform the Repository about the status of relayed deployments.

NotifyFix is called by PageBox DynDns to inform the Repository about archive status changes triggered by the fix of a PageBox setting problem.

RepoQuery implementation class, RepoQueryImpl, implements the RepoQueryIF interface.

GetSubscribers

The GetSubscribers method of RepoQueryImpl calls the GetSubscribers method of RepoSubs and RepoASubs to build a map of the Deploy web service of the subscribed PageBox. Then GetSubscribers returns this map converted to an array.

Notify

The Notify method of RepoQueryImpl calls the Notify method of RepoSubs and RepoASub to update the deployment status of the parameter archive on the PageBoxes in the parameter array.

NotifyFix

The NotifyFix method of RepoQueryImpl calls the NotifyFix method of RepoSubs and RepoASub to update the deployment status of the archives in the parameter array on the parameter PageBox.

RepoSubs

If it doesn’t exist yet, RepoSubs instantiates and initializes a RepoSubs singleton in its public methods, subscribe, unsubscribe, getSubscribers, notify, add, delete and retry, which are static.

The initialization takes place in the init method. init creates a SubscribeBean object and restores subscribers.xml in a subscribers member variable of this SubscribeBean object using the SubscriberHandler SAX handler.

The subscribers map is saved into subscribers.xml by the save method of SaveSubs.

Public static methods except getSubscribers and notify invoke private methods of RepoSubs and SaveSubs:

Public static method

Private method

Class

Caller

subscribe

sub

RepoSubs

Subscribe

unsubscribe

unsub

SaveSubs

Subscribe

getSubscribers

NONE

N/A

RepoQueryImpl

notify

notify2

SaveSubs

RepoQueryImpl

notifyFix

notifyFix2

SaveSubs

RepoQueryImpl

add

addArch/updateArch

SaveSubs

RepoArchs

deltaAdd

addArch

SaveSubs

RepoArchs

delete

delArch

SaveSubs

RepoArchs

retry

retryDeploy

SaveSubs

Retry

getAudit

getAudit

RepoSubs

PageBoxAudit

We describe here the RepoSubs implementation (private method and static methods where a part of the processing takes place). See the SaveSubs section for SaveSubs methods that are shared with RepoASubs.

sub

sub calls the getArchives method of RepoArchs to get the list of the archives defined on the Repository.

sub also creates a map of archive status

Then for each archive sub:

  • Read the archive from Downloaddir

  • Calls the add method of the Deploy Web service

  • Adds an entry to the archive status map with the archive name and the status returned by the Deploy invocation if the archive was deployed

Eventually sub adds a Subscriber to the SubscribeBean’s subscribers map with the subscriber name (user), the archive status map (archives) and an "active" status.

getSubscribers

getSubscribers returns the URLs of the Deploy service of all PageBoxes whose subscriber is the requestor and that have deployed the requested archive.

add

add enumerates the subscribed PageBox from the SubscribeBean’s subscribers map. When the subscribed PageBox already has the added archive with an "installed" status add adds the subscribed PageBox to an updatedSubscribers array. Otherwise add adds the subscribed PageBox to an addedSubscribers array.

Then add calls the SaveSubs’ addArch method with the addedSubscribers array and the SaveSubs’ updateArch with the updatedSubscribers array.

deltaAdd

deltaAdd is called by RepoArchs in case of jardiff upload.

add enumerates the subscribed PageBox from the SubscribeBean’s subscribers map and adds the subscribed PageBox to an addedSubscribers array. Then deltaAdd calls the SaveSubs’ addArch method with the addedSubscribers array.

delete

delete enumerates the subscribed PageBox from the SubscribeBean’s subscribers map. When the subscribed PageBox has the deleted archive delete calls the SaveSubs’ delArch method.

sync

Starting with version 0.0.9 the archive map status map (archives) of a Subscriber only contains deployed archives. Therefore it may happen that a published archive is not deployed on a subscribed PageBox. Unlike a pending state (the Repository unable to contact the PageBox) this is a permanent condition. There is not point to retry the deployment up to the time the PageBox problem is fixed. Unlike the "PageBox err" state the archive wasn’t transferred therefore once the problem is fixed the archive must be deployed and installed on the subscribed PageBox.

For this reason we added a Sync button on the Subscribe form. The sync method is called when the user clicks on the Sync button.

sync enumerates the published archives. When an archive is not defined in archives sync

  • Read the archive from Downloaddir

  • Calls the add method of the Deploy Web service

  • Adds an entry to the archive status map with the archive name and the status returned by the Deploy invocation if the archive was deployed

getAudit

The public static getAudit method calls the private getAudit method.

The private getAudit method:

  • Calls the getArchives method of RepoArchs to get a map of the archives published on the Repository

  • Gets the archive record from the map to retrieve the archive publisher

  • Calls the getAudit method of the Deployer instance to call the DeployIF Web service of the PageBox given in parameter and get the log records for the archive given in parameter

RepoASubs

If it doesn’t exist yet, RepoASubs instantiates and initializes a RepoASubs singleton in its public methods, subscribe, unsubscribe, update, getSubscribers, notify, add, delete and retry, which are static.

The initialization takes place in the init method. init creates a SubscribeBean object and restores asubscribers.xml in a subscribers member variable of this SubscribeBean instance using the SubscriberHandler SAX handler.

The SubscribeBean’s subscribers map is saved into asubscribers.xml by the save method of SaveSubs.

Public static methods except getSubscribers invoke private methods of RepoASubs and SaveSubs:

Public static method

Private method

Class

Caller

subscribe

sub

RepoASubs

ASubscribe

unsubscribe

unsub

SaveSubs

ASubscribe

update

aadd/adelete

RepoASubs

ASelect

getSubscribers

NONE

N/A

RepoQueryImpl

notify

notify2

SaveSubs

RepoQueryImpl

notifyFix

notifyFix2

SaveSubs

RepoQueryImpl

add

addArch/updateArch

SaveSubs

RepoArchs

deltaAdd

addArch

SaveSubs

RepoArchs

delete

delArch

SaveSubs

RepoArchs

retry

retryDeploy

SaveSubs

Retry

getAudit

getAudit

RepoASubs

PageBoxAudit

We describe here the RepoASubs implementation (private method and static methods where a part of the processing takes place). See the SaveSubs section for SaveSubs methods that are shared with RepoSubs.

sub

sub adds the subscriber to the SubscribeBean’s subscribers map.

update

update is called with two main arguments:

  • archs, the archives that were checked by the user on ASelect

  • The Deploy Web service URI of the subscribed PageBox

update retrieves the subscribed PageBox from the SubscribeBean’s subscribers map using the Deploy Web service URI and from there the map of the archives already deployed on this PageBox. Then update enumerates the archives defined on the Repository using the getArchives method of RepoArchs.

If an archive was checked but was not yet deployed on the PageBox, update calls aadd.

In an archive was not checked but was deployed on the PageBox, update calls adelete.

aadd

aadd calls the add method of the Deploy Web service of the target PageBox.

Then it adds an entry to the archive status map with the archive name and the status returned by the Deploy invocation if the archive was deployed.

adelete

adelete calls the delete method of the Deploy Web service of the target PageBox.

Then if the undeployment failed adelete removes the archive entry from the archive status map. Otherwise adelete updates the archive status with the status returned by the Deploy invocation

getSubscribers

getSubscribers returns the URLs of the Deploy service of all PageBoxes whose subscriber is the requestor and that have deployed the requested archive.

add

add enumerates the subscribed PageBox from the SubscribeBean’s subscribers map. When the subscribed PageBox already has the added archive with an "installed" status add adds the subscribed PageBox to an updatedSubscribers array. Otherwise when the subscribed PageBox already has the added archive add adds the subscribed PageBox to an addedSubscribers array.

Then add calls the SaveSubs’ addArch method with the addedSubscribers array and the SaveSubs’ updateArch with the updatedSubscribers array.

deltaAdd

deltaAdd is called by RepoArchs in case of jardiff upload.

add enumerates the subscribed PageBox from the SubscribeBean’s subscribers map and when the subscribed PageBox already has the added archive adds the subscribed PageBox to an addedSubscribers array. Then deltaAdd calls the SaveSubs’ addArch method with the addedSubscribers array.

delete

delete enumerates the subscribed PageBox from the SubscribeBean’s subscribers map. When the subscribed PageBox has the deleted archive delete calls the SaveSubs’ delArch method.

getAudit

The public static getAudit method calls the private getAudit method.

The private getAudit method:

  • Calls the getArchives method of RepoArchs to get a map of the archives published on the Repository

  • Gets the archive record from the map to retrieve the archive publisher

  • Calls the getAudit method of the Deployer instance to call the DeployIF Web service of the PageBox given in parameter and get the log records for the archive given in parameter

SaveSubs

RepoSubs and RepoASubs share the same data format and the same deployment mechanism. Therefore they extend SaveSubs where their common code is implemented.

In this section we present:

  • The class used to describe a subscribed PageBox, Subscriber

  • The SaveSubs methods

subscribers

subscribers is a TreeMap whose key is the URI of the Deploy Web service of a subscribed PageBox and value is an Subscriber object:

class Subscriber {

String state;

String user;

String pbUser;

String pbPasswd;

int rank;

TreepMap archives;

}

Where

  • state is the state of the subscribed PageBox "active" or "pending remove". If the status is "pending remove" the subscribed PageBox must be removed once Retry has successfully undeployed the archive.

  • user is the user ID of the subscriber who subscribed the PageBox

  • pbUser is the ID used by the Repository to authenticate on the subscribed PageBox when the Repository calls the PageBox’s Deploy Web service

  • pbPasswd is the password used by the Repository to authenticate on the subscribed PageBox when the Repository calls the PageBox’s Deploy Web service

  • rank is not used in version 0.0.9. rank grades the suitability of a subscribed PageBox for a Relay role

  • archives is a map whose key is the archive name and value is the deployment status, "installed", "archive err", "PageBox err", "maybe", "pending", "pending remove" or "maybe remove". If the Repository is unable to call the undeployment method of the PageBox Web service (delete) the archive is put in "pending remove" state. If the Repository is unable to call the deployment method of the PageBox Web service (add) the archive is put in "pending" state. Retry retries periodically to deploy the archives in "pending" state and to undeploy the archives in "pending remove" state. If the Repository deploys the archive with relays the archive is in "maybe" state up to the time the Repository receives the corresponding notify call. "maybe remove" is a pending remove state when the archive should be undeployed after relayTime.

Starting with version 0.0.9 the deployment (add) and undeployment (delete) methods of the PageBox Web service return a Status object that contains a diagnosis message and a code that unambiguously describes the error. We describe the status object in the Deployer section. We summarize here the mapping of the Status code onto the subscribers map and the Subscriber’s archives map.

For the deployment (add) method:

Status.code

Action on subscribers and archives

Meaning

OK

archives entry created with a "installed" status

Successful deployment and install

NOTCONTACTED

archives entry created with a "pending" status

"not ready" status. Handled like an unsuccessful call of the PageBox Web service

NOTDEPLOYED

NO archive entry created

The deployment failed. The PageBox was however able to relay the deployment request.

NOTRELAYED

NO archive entry created

The deployment failed and the PageBox wasn’t able to relay the deployment request.

ARCHPB

archives entry created with a "archive err" status

The installation failed because of an archive error. No retry needed. The publisher has to publish a new version of the archive.

PBPB

archives entry created with a "PageBox err" status

The installation failed because of an pageBox error. No retry needed. The PageBox calls the NotifyFix method of the Repository Web service to notify a status change.

For the undeployment (delete) method:

Status.code

Action on subscribers and archives

Meaning

OK

archives entry removed

Successful uninstall and undeployment

NOTCONTACTED

archives entry with a "pending remove" status

"not ready" status. Handled like an unsuccessful call of the PageBox Web service

ARCHPB

archives entry removed

The uninstallation failed because of an archive error. The undeployment was successful.

PBPB

archives entry removed

The uninstallation failed because of an pageBox error. The undeployment was successful.

NOTUNDEPLOYED

subscribers entry removed

The PageBox is corrupted. The Repository unsubscribes the PageBox.

Therefore the Subscriber’s archive map only contains archives actually deployed.

addArch

addArch, updateArch, relay and notify2 are the methods that handle relayed deployment. The mechanism is as follows:

If the archive size is above the ceiling value the sender (the Repository or the relay PageBox) divides the archive list in two chunks. The sender sends the first chunk to the first PageBox in the second chunk. Then it repeats the process up to the time there is only one PageBox to deploy.

addArch and updateArch prepare the initial chunks and relay recursively issue the actual deployments.

notify2 processes the acknowledgements of the relay PageBoxes.

addArch processes an archive deployment to an array addedSubscribers of subscribed PageBoxes.

If the full archive size is below the ceiling value addArch deploys the archive on all PageBoxes in addedSubscribers and adds the archive and its deployment status to the archive status map of the PageBoxes. If the archive size is greater or equal than the ceiling value addArch calls the relay method with the addedSubscribers array.

updateArch

updateArch also processes an archive deployment. The main difference with addArch is that updateArch distributes the archive jardiff. The array of subscribed PageBoxes is called here updatedSubscribers.

If the jardiff size is below the ceiling value updateArch deploys the archive on all PageBoxes in updatedSubscribers and updates the deployment status of the archive in the archive status map of the PageBoxes. If the archive size is greater or equal than the ceiling value updateArch calls the relay method with the updatedSubscribers array.

relay

relay processes a relayed deployment of an archive to an array subs of subscribed PageBoxes. It first builds an array containing PageBoxes randomly chosen in the subs array:

int toRelayNb = subs.size() / 2;

if (toRelayNb > 0) {

uua = new DeployIF.UrlUser[toRelayNb];

Random ran = new Random(System.currentTimeMillis());

for (int i = 0; i < toRelayNb; ++i)

uua[i] = (DeployIF.UrlUser)subs.remove(ran.nextInt(toRelayNb --));

}

Then relay deploys on the first PageBox remaining in the subs array and passes the uua array as parameter: this PageBox becomes a relay and is responsible for the deployment on the uua PageBoxes. If this first deployment fails relay retries a deployment on the next subs PageBox and so on. If the deployment fails on all PageBoxes in subs, relay deploys sequentially on the uua PageBoxes. Otherwise relay calls itself passing the subs array.

delArch

delArch calls the delete method of the Deploy Web service of the target PageBox to undeploy the archive.

If the undeployment is successful delArch removes the archive from the archive status map of the target PageBox. Otherwise delArch updates the status of the archive in the archive status map of the target PageBox to "pending remove" in case of invocation error or if the delete method returned NOTCONTACTED. If the delete method returned NOTUNDEPLOYED delArch unsubscribes the PageBox.

unsub

unsub is called when a PageBox must be unsubscribed. All archives must be removed before except if it is forced unsubscribe.

For each archive deployed on this PageBox unsub

  1. Checks the archive status. If it is "pending" the archive has never been deployed and no undeployment is needed.

  2. Calls the delete method of the PageBox Deploy Web service to undeploy the archive.

If the unsubscribe was forced or if all PageBox archives were unsubscribed, unsub removes the subscribed PageBox. Otherwise unsub sets the state of the subscribed PageBox to "pending remove".

notify2

notify2 is called when a relay PageBox calls the Notify method of the RepoQuery Web service.

notify2 is called with two main parameters:

  1. The archive name

  2. An array of UrlStatus describing the deployment status of the archive on the target PageBoxes

For each subscribed PageBox identified by its Deploy service URI in UrlStatus notify2 updates the status of the archive with the value found in UrlStatus. If the archive is in "pending remove" state (delete before the deployment was completed) notify2 calls the delete method of the PageBox DeployIF Web service and updates the status according to the response of the delete call.

notifyFix2

notifyFix2 is called when a PageBox administrator changed the PageBox setting and retried the installation of archives in "setting pb" state or restarted the PageBox Application server, the change resulting in a state change state to "installed" or "archive err". This change triggers the invocation of the NotifyFix method of the RepoQuery Web service.

notifyFix2 is called with two main parameters:

  1. The PageBox whose setting problem was fixed

  2. An array of FixArch describing the deployment status of the archives on the fixed PageBox

notifyFix2 first retrieves the Subscriber object describing the fixed PageBox. Then for each archive in the FixInfo array notifyFix2 retrieves the ArchiveStatus object describing the archive and updates its status member.

retryDeploy

retryDeploy is called in a worker thread to retry deployments and undeployments and eliminate pending and maybe conditions.

For each subscribed PageBox and for each archive deployed in this subscribed PageBox retryDeploy analyzes the archive status. If the status is "pending" or if the status is "maybe" for more than relayTime retryDeploy calls the add method of the Deploy Web service to deploy the archive and updates the archive status according to the Deploy response. If the status is "pending remove" or if the status is "maybe remove" for more than relayTime retryDeploy calls the delete method of the Deploy Web service to undeploy the archive. If the undeployment is successful retryDeploy removes the archive entry from the archive status map. Otherwise it sets the archive status according to the Deploy response. If a subscribed PageBox was in state "pending remove" and if its archive status map is empty retryDeploy removes the subscribed PageBox.

SubscribeBean

getSubscribers

SubscribeBean implements a static getSubscribers method, which is called by subscribe.jsp.

getSubscribers retrieves the RepoSubs instance and then the embedded SubscribeBean instance.

Eventually getSubscribers calls the getSubs method of SubscribeBean.

getASubscribers

SubscribeBean implements a static getASubscribers method, which is called by asubscribe.jsp.

getASubscribers retrieves the RepoASubs instance and then the embedded SubscribeBean instance.

Eventually getASubscribers calls the getASubs method of SubscribeBean.

getArchives

SubscribeBean implements a static getArchives method, which is called by aselect.jsp.

getArchives retrieves the RepoASubs instance and then the embedded SubscribeBean instance.

Next getArchives determines if the user requested a date sort with getParameter("date"): Two buttons are defined on aselect.jsp to allow sorting by archive name or by publication date. When the user clicks on the date button a date parameter is set.

Eventually getArchives calls the getArchs method of SubscribeBean.

getSubs

getSubs returns an HTML string that contains a table row per PageBox subscribed on the Repository by the same user as the requestor or by any user when the user is in a pagebox-admin role. These PageBox data are stored in the SubscribeBean’s subscribers map.

getASubs

getSubs returns an HTML string that contains a table row per PageBox archive-subscribed on the Repository by the same user as the requestor or by any user when the user is in a pagebox-admin role. These PageBox data are stored in the SubscribeBean’s subscribers map.

The difference between getSubs and getASubs is that rows returned by getASubs contain an additional field, which is a link to select.jsp.

getArchs

getArchs returns an HTML string that contains a table row per archive published on the Repository. These rows can be sorted either by archive name or by publication date.

getArch is called with a subscriber parameter that contains the name of the current subscribed PageBox. getArch uses this name to retrieve the corresponding Subscriber object from the subscribers map.

Each row contains a checkbox. getArchs marks this checkbox as checked when the archives map of this Subscriber contains the archive.

In case of sort by date getArchs populates a temporary TreeMap tm with the content of archives. The tm key is a DateArch object.

In case of date sort getArchs implements a sort of toggle. The first time the user clicks on date a order boolean is false, which means that archives must be sorted the most recent first. Then getArchs writes a hidden order field in the HTML string. The next time the user clicks on date getArchs retrieves this parameter. Then getArchs sorts the archives the oldest first. The ordering is actually implemented in the compareTo method of DateArchs.

Deployer

To call the Deploy Web service subscribe methods uses a Deployer class.

Deployer implements a DeployerIF, which is defined like this:

interface DeployerIF extends DeployIF {

void setUrl(String url, String user, String passwd);

}

The DeployerIF interface implements the DeployIF interface, which is defined like this:

interface DeployIF {

Status add(String arch, String downloadURL, String owner,

byte[] archData, String date, String docURL, String user, boolean isUpdate,

boolean runInstall, UrlUser[] relayed);

Status delete(String arch, String downloadURL, String owner,

String user, boolean runRemove , boolean keepDir);

String rename(String oldDownloadURL, String newDownloadURL, String user);

String getArchPath(String arch);

String getAudit(String arch, String owner, String user, String downloadURL);

}

Where Status is defined like this:

class Status {

final static int OK = 0;

final static int NOTDEPLOYED = 1;

final static int NOTRELAYED = 2;

final static int ARCHPB = 3;

final static int PBPB = 4;

final static int NOTUNDEPLOYED = 5;

final static int NOTCONTACTED = 6;

int code;

String msg;

}

Where code can take an OK, NOTDEPLOYED, NOTRELAYED, ARCHPB, PBPB, NOTUNDEPLOYED or NOTCONTACTED value and msg is a diagnosis message.

add deploys an archive, delete undeploys an archive, rename allows changing the download URL (the URI of the RepoQuery Web service) for all archives deployed on a PageBox, getArchPath returns the URL of an installed archive and getAudit returns the log records of a PageBox about an archive installation.

Look at the PageBox implementation for more information.

Deployer is a facade class that forwards its requests to an actual deployer. A Deployer factory method has a parameter, which is the full class name of the actual deployer. The factory creates the actual deployer and its facade with the following snippet:

Class iclass = Class.forName(deployerclass);

DeployerIF di = (DeployerIF)iclass.newInstance();

return new Deployer(di);

The class name of the actual deployer is defined in a deployer-class initialization parameter. This deployer must implement a constructor without parameter and the DeployerIF interface. Three deployers are available:

  • JWSDPDeployer

  • AxisDeployer

  • HTTPDeployer

Starting with PageBox 0.0.11 Deployer, JWSDPDeployer, AxisDeployer and HTTPDeployer support token methods, DeployIF extending the TokenIF interface defined like this:

interface TokenIF {

DeployIF.Status frameSend(TokenFrame frame) throws RemoteException;

}

We describe these methods in the Token API implementation guide.

JWSDPDeployer

JWSDPDeployer uses the JAX-RPC implementation included in the Java WSDP.

The JWSDPDeployer constructor instantiates a Deploy stub generated with wscompile –gen:client.

The setUrl method of JWSDPDeployer sets the URL of the target PageBox Deploy service.

For each method of the DeployIF interface JWSDPDeployer calls the corresponding method of the stub.

We had a problem with JWSDP 1.2 for the Token API method, frameSend. It seems that this version doesn’t handle properly linked lists (translated in SOAP sequences) of objects. We did not retry with JWSDP 1.3 and JWSDP 1.5. The TokenFrame class used by the frameSend method is defined like this:

class TokenFrame implements Serializable {

String repUrl;

int nb;

LinkedList adjacencyList;

LinkedList msgList;

}

Therefore we serialize and unserialize the adjacencyList and msgList entries with the ser and unser methods.

AxisDeployer

AxisDeployer uses the Axis JAX-RPC implementation.

AxisDeployer has a service member variable initialized like this:

DeployServiceLocator service = new DeployServiceLocator();

DeployServiceLocator is a factory class allowing instantiating the stub class, DeploySoapBindingStub with the Web service URL. DeployServiceLocator, DeploySoapBindingStub and a set of helper classes are generated with org.apache.axis.wsdl.WSDL2Java.

The setUrl method of JWSDPDeployer calls the getDeploy method of DeployServiceLocator to instantiate a DeploySoapBindingStub.

For each method of the DeployIF interface AxisDeployer calls the corresponding method of the stub.

HTTPDeployer

HTTPDeployer uses HTTP and calls a HTTPDeploy servlet implemented on target PageBoxes.

To the opposite of JWSDPDeployer and AxisDeployer HTTPDeployer doesn’t use JAX-RPC and XML.

The setURL method of HTTPDeployer stores:

  • The URL of the target HTTPDeploy servlet

  • The base 64 encoded string that contains the user name and password to use when the target HTTPDeploy servlet is protected by basic authentication

For each method of the DeployIF interface HTTPDeployer opens a HTTP connection to the target HTTPDeploy servlet, serialize the method parameters and parses the HTTPDeploy servlet response.

The first field in the POST request contains a verb that depends on the DeployIF method:

DeployIF method

Verb

add

ADD

delete

DELETE

rename

RENAME

getArchPath

GETARCHPATH

getAudit

GETAUDIT

frameSend (from TokenIF)

FRAMESEND

HTTPQuery

When JAX-RPC is used messages are parsed by a servlet part of the JAX-RPC implementation, JAXRPCServlet in case of JWSDP and AxisServlet in case of Axis. In case of HTTP implementation we must provide this servlet.

HTTPQuery has a RepoQueryImpl member instantiated on startup.

The init method of HTTPQuery calls the init2 method of the RepoQueryImpl instance.

The destroy method of HTTPQuery calls the homonymous method of the RepoQueryImpl instance.

HTTPQuery expects POST requests and therefore implements the doPost method.

doPost reads the first field of the request, which is the verb and calls a parsing method according to its content:

Verb

HTTPQuery method

GETSUBSCRIBERS

GetSubscribers

NOTIFY

Notify

NOTIFYFIX

NotifyFix

FRAMESEND

frameSend (from TokenIF)

TKREGISTER

tokenRegister (from RepoTokenIF)

TKUNREGISTER

tokenUnregister (from RepoTokenIF)

GetSubscribers unserializes the remaining parameter, calls the homonymous method of the RepoQueryImpl instance, serializes and returns its response. Notify unserializes the remaining parameters and calls the homonymous method of the RepoQueryImpl instance. NotifyFix unserializes the remaining parameters and calls the homonymous method of the RepoQueryImpl instance

Retry

The Retry class extends Thread. Its constructor instantiates a DynDns object and starts the worker thread.

Its run method (invoked in the worker thread) periodically calls the retry methods of RepoSubs and RepoASubs. These methods call the retryDeploy method of SaveSubs where the core of the retry handling is implemented.

The run method also calls the register method of the DynDns object to:

  • Register the Repository host on the DynDns DNS server if the Repository host is unknown

  • Update the IP address of the Repository host on the DynDns DNS server if the address has changed

A single Retry object is instantiated in the init method of Subscribe. Therefore the init method of Subscribe must be called when the Repository Web application is loaded (<load-on-startup>0</load-on-startup> in web.xml).

DynDns

Background

Permanent IP addresses are scarce resources especially in Europe and Asia. The cheapest way to get a permanent IP address is to use the service of an Application Service Provider (ASP). An Internet Service Provider (ISP) buys ranges of IP addresses and allocates these addresses to its customers. Depending on the customer subscription the ISP may allocate a temporary address to a customer. It is usually the case with cable and DSL subscribers and sometimes the case of dial-up subscribers.

Users usually don’t know the IP address of a site. Users know the DNS name of the site. A site owner may choose to buy a domain name (about $10 to $20 per year). Buying a domain makes sense for commercial reasons: the site URL is easier to remember and shows a minimal commitment of the provider. For technical sites (syndicated content provider, Web service) a sub-domain name that we usually can get for free is usually enough.

DNS servers handle matching tables:

DNS name

IP address

When the IP address of a host changes (which is likely to happen in case of temporary allocated IP address) it is possible to dynamically update its IP address on the DNS server.

PageBoxes, PageBox Repositories and Web applications deployed with PageBox need permanent host names. They don’t require domains and permanent IP addresses. Using temporary IP addresses and sub-domain names actually augments the value of PageBox by enabling server-less constellations:

In this scenario the users host the Application server and the PageBox on their workstation and the publisher hosts an Application server and a Repository on her workstation. Workstations get temporary IP addresses from the users and publisher ISPs. The PageBoxes and the Repository monitor the temporary IP address and update the Dynamic DNS server in case of change.

  1. The users subscribe to the publisher’s Repository using the publisher’s sub-domain name

  2. The publisher publishes her Web application on her local Repository. The Repository automatically installs the Web application on the subscribers’ Application servers using the subscribers’ sub-domain names

  3. Users query their local Web application instance

Using a local Web application instance can be useful for compute-intensive applications (stock trading) or in cases where the user must be able to use the application in disconnected mode (off-line learning). It is obviously also possible to run one Application server and one PageBox per office connected with DSL or WiFi.

DNS servers typically support dynamic update with the RFC 2136 protocol. We chose in PageBox version 0.0.8 and above to support the DynDNS HTTP protocol. This protocol is supported by firewalls. Furthermore the DynDNS site offers free sub-domain registration and update. In the future we may implement a DynDNS / RFC 2136 gateway.

Implementation

The DynDNS support is implemented in the DynDns class.

This class implements three methods, a constructor, a register and a getHost method.

The DynDns constructor first read configuration parameters such as the URL of the registration and update page, authentication parameters, the DNS name to register and either the name of the interface or a local name whose address must be used. If the last configuration parameter is the interface name the constructor retrieves the IP address with NetworkInterface.getByName(inter). If the last configuration parameter is a local name the constructor retrieves the IP address with InetAddress.getByName(name).

DynDNS.org policy is to forbid unneeded queries. Therefore DynDns persists the registered domain and address in a dyndns.txt file. This file contains three lines:

  • The URL of the registration and update page

  • The registered DNS name

  • The registered IP address

The DynDns constructor reads and caches the dyndns.txt file.

The register method:

  1. Retrieves the current IP address

  2. Checks if the (DNS name, IP address) is not already registered on the DynDNS server using the dyndns.txt cache

  3. Queries the DynDNS server, checks the server response and updates dyndns.txt if the (DNS name, IP address) is not yet registered

We wrote a small DynDns emulator to test the implementation.

Servlet source

Update.java

Web application

dyndns.war

The getHost method returns:

  • The DNS name of the host if the DNS registration was performed

  • The local name otherwise

The getHost method is called by the SaveSubs' adjustDownloadURL to build the download URL sent to PageBoxes.

Contact:support@pagebox.net
2002-2004 Alexis Grandemange. Last modified .