PageBox |
|
|
|
|
|
Presentation | Download | User guide | Implementation | Epimetheus | euroLCC | Prometheus |
Pandora implementation
This document presents the implementation of Pandora.
Pandora is not a comprehensive application. Pandora fully implements aspects specific to a PageBox-enabled application but it doesn’t implements things like an administration and a mean allowing adding new articles. This limitation makes the implementation easier to understand and adapt. Pandora can be used as the skeleton of a real application.
Programmers who want to implement a PageBox-enabled Web application or adapt an existing application for a PageBox deployment.
Model View Controller (MVC) architecture
Data stored in a RDBMS and accessed using JDBC
Authentication by trusted referrers that communicate with Pandora using HTTP POST
SOAP Web service for the communication with the central server
The reference implementation of Pandora uses the MySQL database (tested with MySQL 3.23.53) and the MySQL Connect/J 2 JDBC driver.
Pandora uses three tables:
command
item
article
article describes the referenced articles. An article has a unique ID, a name and a unit price. The article table also contains the available number of articles (qty) and the number of article sold on this Pandora instance.
command describes the commands. A command has a unique ID, data allowing charging a customer (name), data allowing delivering the command (addr,) the URL of the Payment facility Web service (payment,) the URL of the Delivery facility Web service (delivery) and the date when the command was made. name can contain the user ID, the user name, a billable account or an identifier returned by a payment facility. addr can contain the user address and mail address, the user office # and telephone #. Pandora doesn’t interpret the content of name and addr: the name and addr fields can have different formats on different Pandora instances. The only requirement is that the name format is understood by the payment facility and that the addr format is understood by the delivery facility.
item describes an item of a command. An item is uniquely identified by a command ID and by an article ID. An item has also a number of articles (qty.)
The Pandora deliverables include:
The distributed application - the core of the Pandora demo - that implements the user interface
The referrer site page that links to Pandora and sends the payment and delivery information
The central application that maintains the number of articles available and calls the payment and delivery facilities
A mockup of payment and delivery facility
Pandora uses the PageBox API. See the PageBox API documentation for more details.
The distributed application implements a user interface that allows customers creating commands, adding, updating or deleting items in the command and committing commands.
The distributed application periodically sends the filed commands to the central application.
Then the central application issues payment and delivery requests and returns an updated inventory to the distributed application.
PageBox version 0.0.7 implement a Java 2 security. With this security a deployed application cannot read and write files everywhere, run commands, use native code or create and use a class loader. Sandbox checking is a working example allowing PageBox administrators to check that the security is properly set. The distributed and the central applications and the payment/delivery mockup call this facility.
CheckSandbox exercises the sandbox in its constructor.
The constructor calls the following methods:
checkRead to try reading a file
checkWrite to try writing a file
checkCmd to try running a command
checkLoad to try loading and using native code
checkClassLoader to try creating a class loader and using it to load a class
checkRead tries to read the file whose path was given as parameter and to log lines found in the file.
checkWrite tries to write "CheckSandbox" in the file whose path was given as parameter and then to delete this file.
checkCmd uses the Runtime to try executing the command given as parameter and waiting for its completion.
checkLoads tries calling the static write method of the CheckLoad class.
checkClassLoader tries
Instantiating a CheckClassLoader
Using this instance to load pandora.CheckLoaded
Instantiating the pandora.CheckLoaded class
CheckLoad implements a minimalist native interface:
public class CheckLoad { private static native void writeLine(String line); public static void write() { writeLine("CheckLoad"); } static { System.loadLibrary(CheckSandbox.chkLib); } } |
loadLibrary loads the library defined in CheckSandbox.chkLib when write is called for the first time (when the statics of CheckLoad are created.)
CheckClassLoader extends ClassLoader. Its most important class is loadClass that gets called each time a program (or the JVM) requires a class load. loadClass loads classes from the directory given to the constructor and caches them in a classes map for subsequent requests.
The distributed application contains eight components:
The user interface view, command.jsp
The user interface model, CommandBean.java
The user interface controller, CommandCtrl.java
A component that periodically updates the central application, Publish.java
The installation class, Install.java
A view to display the URLs of the distributed application installed from the same Repository, neighbor.jsp
A model to display the URLs of the distributed application installed from the same Repository, NeighborBean.java
A controller to display the URLs of the distributed application installed from the same Repository, NeighborCtrl.java
CommandCtrl retrieves a CommandBean instance from the Session, parses the user request and updates the CommandBean instance. Then CommandCtrl forwards the request to common.jsp using RequestDispatch.forward.
common.jsp retrieves the CommandBean instance from the Session and calls its properties to display the form.
NeighborCtrl
Calls the getClones method of PageBoxAPI
Creates a NeighborBean with the list returned by getClones
Adds this NeighborBean instance to the request attributes
Forwards the request to neighbor.jsp
neighbor.jsp retrieves the NeighborBean instance from the request attribute and calls its getClones method to display the URLs of the distributed application installed from the same Repository.
command.jsp shows a form that:
Displays the command items
Allows selecting an article on a dropdown list
Allows adding, updating and deleting command items
CommandBean implements the following read-only properties for command.jsp:
Articles. getArticles returns a set of Javascript instructions price[i]="price_i"; where i is the article ID and price_i is the price of the i article. When the user selects a different article a Javascript snippet in command.jsp uses this array to update the current article price.
Options. getOptions returns a set of HTML options <option value="i">name_i</option> where i is the article ID and name_i is the name of the article. Options is used to populate the Article dropdown list.
Items. getItems returns a set of HTML rows <tr><td><button name="delete" onClick=del("i")>Delete</button></td><td><button name="update" onClick=upd("i")>Update</button></td><td>name_i</td><td>qty_i</td><td>price_i</td></tr> where i is the article ID, name_i is the name of the article, qty_i is the number of articles i in the command and price_i is unit price of i * qty_i.
UpdatedId, Disabled, Qty and Price. These properties are used to handle updates. UpdatedId contains the ID of the updated item and is used by CommandCtrl to interpret the request. Disabled returns disabled="true" in updates to disable the article selection. Qty returns the current number of articles in the command. Price returns the price of the current article.
CommandBean implements its properties using an article map called articles and an item map called items.
CommandCtrl is a servlet and implements the servlet init, doGet and doPost methods.
Both doGet and doPost implement the object creation logic:
Check if the user is authenticated in the checkReferrer method
Retrieve the distributed application directory
Instantiate PageBoxAPI the first time CommandCtrl.doGet or CommandCtrl.doPost is called
Instantiate Publish the first time CommandCtrl.doGet or CommandCtrl.doPost is called
Instantiate CommandBean in a createCb method when the user enters a new session
createCb uses the PageBox API to read the article list from the database and initiate the CommandBean instance. Then doGet and doPost store the CommandBean instance in the Session object.
The distributed application uses HTTP POST. Therefore doPost must also parse the user request. The user request can be:
Adding a new article to a command. In this case doPost calls an add method.
Updating (changing the number of articles) a command. In this case doPost calls an update method.
Deleting an article from a command. In this case doPost calls a delete method.
Committing a command: updating the command and item tables in order to persist the command. Up to the commit the command data are only stored in CommandBean. In this case doPost calls a commit method.
Before looking at these methods we must present how CommandCtrl handles the database tables.
The fields of the command record are set from the authentication data retrieved by the checkReferrer method.
The fields of the item records are set from the items Map of CommandBean. The keys of this Map are the article Ids and the values of this Map are Item objects.
The article records are copied in an articles Map of CommandBean. The keys of this Map are the article IDs and the values of this Map are Article objects.
The Article class is defined like this:
class Article { String name; float price; int qty; } |
Where:
name is the article name
price is the unit price of the article
qty is the number of articles available for sale
Initially the article table and therefore the Articles objects contain what the installation class has set. Then each time it calls the Update Web service Publish gets an updated article list and updates the article table. The distributed application cannot sell more than qty but when concurrent customers ask for the same article in different sessions. See the presentation of the demo for a full explanation of this mechanism.
The Item class is defined like this:
class Item { int qty; } |
Where qty is the number of articles with a given article ID in the command.
update retrieves the Item object corresponding to the selected item and updates its qty field with the number entered by the user. update rejects the update request if the requested quantity is higher than the qty of the article.
If there is already an Item object for this article, add adds to its qty the number entered by the user. Add rejects the add request if the total quantity is higher than the qty of the article.
If there is no Item object for this article, add creates an Item object and sets its qty with the number entered by the user. add rejects the add request if the requested quantity is higher than the qty of the article.
delete removes the Item object corresponding to the selected item from the items Map.
The distributed application doesn’t implement a authentication mechanism of its own or provided by the Application server. Instead it trusted referrer sites that can prove their identity.
CheckReferrer first checks if the user information is already stored in the Session.
User information is made of four fields and four Session attributes:
CommandCtrl field | Session attribute | Meaning |
---|---|---|
user | user | Data that uniquely identifies the user and allows charging it with the payment facility |
addr | addr | Data that uniquely identifies the user and allows to deliver the article to it using the delivery facility |
payment | payment | Web service URL of the payment facility |
delivery | delivery | Web service URL of the delivery facility |
If the user information is not yet stored in the Session checkReferrer assumes that the user is coming from a trusted application that has properly set the user information in HTTP POST data and proven that it is a trustable referrer.
checkReferrer expects that the user request contains six fields:
name (user in the Session and in CommandCtrl)
addr
payment
delivery
certificate: the certificate used to sign the name field
signature: the signature of name with the certificate
checkReferrer verifies that the name is signed with the certificate passed in parameter, which implies that the referrer owns the private key corresponding to the certificate. The referrer site page shows an example of valid link to the distributed application.
Then checkReferrer stores the user information in Session attributes.
Note:
In a production environment you must also check that the certificate was issued by a trusted authority.
commit records the command stored in CommandBean in the command and item tables. It also updates the article table.
commit
Insert a new command record
Retrieves the unique command ID
For each Item object in the items map inserts a new item record and updates the corresponding article record: the quantity sold = former quantity sold + qty and the quantity to sell = former quantity to sell - qty
Publish extends Thread: its run method runs in an independent thread created when Publish is instantiated at the first invocation of CommandCtrl. Publish sends the commands recorded in the command and item tables in batch to the central server using an Update Web service.
The Update Web service implements the UpdateIF interface:
interface UpdateIF extends Remote { class Item { int id; int qty; } class Command { String name; String address; String payment; String delivery; long date; Item[] items; } class Article { int id; String name ; float price; int qty; } Article[] update(Command[] orders) throws RemoteException; } |
Publish has three methods, its constructor, its run method and an update method.
The Publish constructor retrieves the URL of the Update Web service in a central.txt file.
Then it instantiates an Update Web service stub, sets its URL and starts the Thread. Then run is called.
run calls the update method every period. period is a context parameter.
update
Reads the commands from the command table.
For each command creates and sets a Command object
For each item in the item table whose cdid = the command ID creates and sets a Item objects in the items array of the command object
Calls the update method of the Web service
If the update call was successful clears the command and item tables
Update the article table with the Article array returned by the update call
Note:
In a production implementation the article table would probably contain a validity range:
An article would have a price and an available quantity between a date A and a date B.
It would be possible to remove an article.
These functions are easy to implement but wouldn’t improve the clarity of the Pandora example.
The Install class implements the InstallIF interface and is called by the PageBox once the archive has been installed on the target Application server and before the archive is dynamically deployed on the Application server. For more information about the Installation interface see the developer guide and the installation guide.
The same installation class is used by all applications. However only the distributed and the central application need a database update. The table below summarizes the use of the installation class features by the applications:
Web application | Database update | web.xml update |
---|---|---|
Distributed application | yes | yes |
Central application | yes | yes |
Payment or delivery application | no | yes |
If it doesn’t contain the update placeholder the application web.xml file is not updated.
If the PageBox administrator has not configured the JDBC info in rules.xml, the database update is not performed (case of the payment and delivery applications.)
The install method is a method of the InstallIF interface. The install method is called by PageBox to perform a post installation.
In case of update the install method of the Pandora installation class drops the article table with the dropArticle method and re-create/populate the article table with the createArticle method.
In case of new installation the install method of the Pandora installation:
Updates the archive’s web.xml with the updateWebXml
Drops all Pandora tables with the drop method and re-create the tables with the create method
The uninstall method is a method of the InstallIF interface. The uninstall method is called by PageBox to perform a de-installation.
In case of de-installation for update uninstall does nothing.
Otherwise uninstall drops all Pandora tables with the drop method.
drop calls dropArticle to drop the article table and drops the command and item tables.
create creates the command and item tables and calls the createArticle method to create and populate the article table.
dropArticle drops the article table.
createArticle creates the article table. Then it reads an article.csv file from the archive directory.
article.csv must contain four columns separated by commas:
article ID that must be an integer
article name, a string
article price that is parsed as a float: enter it in a format iii.jj
article number that must be an integer
For each row in the article.csv file createArticle inserts a record in the article table.
updateWebXml illustrates configuration updates in installation classes.
The web.xml is expected to contain a workdir context parameter with an update placeholder:
<context-param> <param-name>workdir</param-name> <param-value><!--Install_wordir--></param-value> </context-param> |
updateWebXml replaces <!--Install_wordir--> by the archive directory.
Here is how it works. updateWebXml
Checks that web.xml exists at the expected location archive_directory/WEB-INF/web.xml
Creates a new configuration file web2.xml
Reads web.xml and looks for <!--Install_wordir-->
Rewrites on web2.xml but when it finds <!--Install_wordir-->. In that case updateWebXml writes the path of the archive directory
Renames web2.xml into web.xml
The referrer site pages are designed to simulate an actual authentication mechanism.
The referrer environment is made of three components:
login.jsp that allows the user to enter authentication information
link.jsp that implements a link to the distributed application and is the view of the link
LinkBean.java that computes the signature of the referrer site and is the model of the link
login.jsp displays a form with four text area:
name: data that uniquely identifies the user and allows charging it with the payment facility
address: data that uniquely identifies the user and allows to deliver the article to it using the delivery facility
payment: Web service URL of the payment facility
delivery: Web service URL of the delivery facility
When the user clicks on the Login button login.jsp stores these data in Session attributes.
link.jsp implements two mechanisms:
Automatic redirect to the distributed application’s CommandCtrl
Link to the distributed application’s CommandCtrl
When it is called link.jsp retrieves the user name from a Session attribute, instantiates a LinkBean object and sets its name property.
Both mechanisms described above are actually implemented in a goto script and in a PandoraForm hidden form. The automatic redirect is triggered by <body onload="goto();"> whereas the link is triggered by <a onClick="goto();"
goto is implemented like this:
function goto() { document.PandoraForm.action="command"; document.PandoraForm.submit(); } |
action contains the URL of CommandCtrl. In the code above we can set a relative URL because the Referrer site components are packaged in the distributed application. On production systems the referrer site and the distributed applications are not hosted by the same Web application and often even by the same Web site. In these cases the URL has to be a full URL.
To pass the information needed by CommandCtrl.checkReferrer the PandoraForm hidden form is coded like this:
<form name="PandoraForm" method="post"> <input type="hidden" name="msg" value='<%= lb.getMessage() %>' /> <input type="hidden" name="certificate" value='<%= lb.getCert() %>' /> <input type="hidden" name="signature" value='<%= lb.getSignature() %>' /> <input type="hidden" name="name" value='<%= name %>' /> <input type="hidden" name="addr" value='<%= session.getAttribute("PandoraAddr") %>' /> <input type="hidden" name="payment" value='<%= session.getAttribute("PandoraPayment") %>' /> <input type="hidden" name="delivery" value='<%= session.getAttribute("PandoraDelivery") %>'/> </form> |
Where lb is the LinkBean instance.
LinkBean implements a constructor, a Name write property and two read properties, Cert and Signature.
The LinkBean constructor takes four parameters:
The name of a key store
The password of this key store
The alias in the key store of the (private key, certificate) to use to prove its identity to the distributed application
The password of this alias
Note:
In a production environment the keystore and the (name, password) uples should not be accessible even in read mode to prevent security and repudiation problems. In the future we plan to support the automated distribution of link pages with PageBox. These pages will then be used in existing Web applications. It can imply some enhancements of the installation process.
Here is an example of invocation from link.jsp:
LinkBean lb = new LinkBean("E:\java\pandora\keystore", "kspasswd", "mycert", "mypasswd"); |
The constructor loads the key store and retrieves the certificate and private key from the key store.
setName stores the name in a member variable.
getCert returns the certificate.
getSignature signs the name with the private key and returns the signature.
The central application
Implements the Update Web service
Uses the same table definitions as the distributed application
Uses the PageBox API
Therefore the central application can also be deployed using PageBox and the same installation class as the distributed application.
The main part of the central application is the implementation of the Update Web service, UpdateImpl.
UpdateImpl implements the UpdateIF and the ServiceLifecycle interfaces.
The main methods of UpdateImpl are init and update.
init is a method of the ServiceLifeCycle interface. init is called just before the first Web service invocation.
init retrieves the directory of the Central application from a context variable, workdir.
Then init creates a PageBox API instance and creates a stub for the Query Web service of the payment and delivery facilities.
update is the method of the UpdateIF interface (and of the Update Web service.)
update uses the PageBox API to get a database connection.
Then for each Command object in the orders array update:
Sets the URL of the Query Web service to the payment URL of this command
Calls the payment Web service with the name of this command as parameter
Sets the URL of the Query Web service to the delivery URL of this command
Calls the delivery Web service with the name of this command and an items array as parameters
Inserts a new record in the command table
For each item in the items array of the Command object inserts a new record in the item table
Updates an arts Map that contains an Art object for each article included in these commands
The Art class is defined like this:
class Art { int qty; int sold; } |
update stores in the arts map the quantity of articles sold and still available for sale.
Next for each article in arts update updates the quantity sold and available in the corresponding record in the article table.
Eventually update reads the article table to build the Article array returned by the Web service.
The payment and delivery mockups implement the Query Web service and use the PageBox API. Therefore the payment and delivery mockups can also be deployed using PageBox and the same installation class as the distributed and central applications.
The Query Web service has the following interface:
interface QueryIF extends Remote { class Item { int id; String name; float price; int qty; } class Status { int rc; String msg; } Status query(String id, Item[] items) throws RemoteException; } |
Where
id is the data that allows charging the customer (payment) or delivering the good
Each item object contains the article ID, the name of the article, its unit price and the requested number of articles (qty)
rc can contains an error code (< 0) or a tracking number
msg can contain additional status information
The main part of the central application is the implementation of the Query Web service, QueryImpl.
QueryImpl implements the QueryIF and the ServiceLifecycle interfaces.
The main methods of QueryImpl are init and query.
init is a method of the ServiceLifeCycle interface. init is called just before the first Web service invocation.
init retrieves the directory of the Central application from a context variable, workdir.
Then init creates a PageBox API instance.
query is the method of the QueryIF interface (and of the Query Web service.)
In this mockup query simply logs the request and returns a fake status.
Contact:support@pagebox.net
©2002-2004 Alexis Grandemange.
Last modified
.