The following are notes for developers wishing to add new material to the WBUI. The various components of the interface are described, and what is involved in common tasks such as adding new Web forms. Debugging can be a challenge; various tools for that in the browser and Dojo environment are described. Another page describes the interface from the user's point of view. A large amount of information about the current implementation has been necessary to give the background necessary for adding new functionality.
A number of components contribute to the WBUI. They are described from the standpoint of this specific implementation; links to general information about each component are provided. The server side (Tomcat and servlets) is covered first.
Apache Tomcat 5.5 is used as the Web server and acts as a container for the Java servlets. Note that the Web service interface also makes calls to servlets within an Axis2 service contained within the same Tomcat instance.
The client (the web directory under the main directory) and server side of the WBUI are deployed into Tomcat with the Ant "deploywbui" target in the main directory.
Java servlets (located in src/net/es/oscars/servlets) handle all requests from the client. Reservation-related servlets reformat incoming parameter values and make the same requests to the BSS as the Axis2 Web service servlets do. AAA administration is only available through the WBUI-related servlets and not the Web service.
All security is handled by the server side, by calls within the servlets to the AAA for user authentication and authorization. Login's require a login name and a password. The OSCARS authorization policy is used, given an authenticated login name. Some client-side functionality, such as some tabs, are normally available only after server authorization, but could be easily displayed by someone intent on circumventing that. In such a case, submission of forms normally requiring authorization to see would result in AAA errors returned from the servlets.
The results of all calls to the AAA or BSS are returned via commented JSON formatted data. The MIME type returned is "text/json-comment-filtered". They are commented to avoid certain cross-site scripting attacks. When Dojo 1.2 is available, a more secure method than this may be used. The section on Javascript modules covers the communication between the client and server in more detail.
Servlet classes are mapped to URL's in web/WEB-INF/web.xml. One section of this file sets up mappings between servlet names and servlet classes, and the other sets up a mapping between the servlet name and a relative URL which is used in client requests.
We are currently using (and redistributing) version 1.1.1 of the Dojo toolkit. It is automatically installed as part of the WBUI deploy. It is extensive, and includes a large library of widgets, Ajax handling, JSON handling, event handling, etc. It is cross-platform, but its Internet Explorer support, while improving, is not enough yet to run OSCARS using that browser.
The Book of Dojo contains a large amount of information on using Dojo, actually a somewhat daunting amount of information. The current WBUI uses a relatively small number of Dojo's features. One feature of note that is used are Dojo grids, which while experimental, are used in the WBUI for sortable, clickable tables. The book section on Dijit contains information on all the widgets used. More information on how Dojo is used in the WBUI is provided in the following sections.
web/index.html is the starting page in the WBUI, accessible via https://oscars.es.net/OSCARS/. It is also in effect the only page in the WBUI. The displayed portion contains a banner at the top indicating some of the organizations currently participating in developing OSCARS, a line containing the current date and time as well as the current status of the client, and a set of tabs and their content. Before successful login, only the Login/Logout tab is displayed. After login, more tabs appear, depending on user authorizations (see the page on the user interface for a general description of the tabs). At the bottom of the page are links pointing to the documentation, and other general information. These will open up in separate browser tabs. Following that are two email links.
It is very important to note that the back/forward button should not be used while in the WBUI. The reason for this is described after the following sections describing the source for the document.
Going through the source for the page, the first item is the DOCTYPE. Including the DOCTYPE, as is, is important for the proper functioning of some Dojo widgets.
The next section contains all the CSS style sheets used. The first is necessary for proper styling of grids, the next two are the standard Dojo style sheets, and the last is a local style sheet containing rules that may override the Dojo rules. More on style sheets is contained in the section on them below.
The first script section includes the required main Dojo Javascript module, and contains configuration indicating that Dojo widgets should be parsed when a page is loaded, and a property that would be used if back/forward worked (preventBackButtonFix)
The next two script sections register the namespace for the WBUI Javascript modules (oscars), and make the modules available (oscars.*). More on the modules is provided in the section below.
The next script section includes (a long list of dojo.requires) the modules for all the Dojo widgets used within the WBUI (dijit.* and dojox.*). The dojo.* modules provide core functionality as well a module (ItemFileReadStore) used for data by one kind of grid.
OSCARSState is the declaration for a class used for a global variable.
The following script section uses dojo.addOnLoad several times. This sets up method calls that happen once, after all the widgets in index.html have been parsed. oscars.DigitalClock.initClock initializes the date and time display in the status bar. The next addOnLoad sets up the method to call when a tab is clicked on. The final one is not currently functional.
Dojo depends a great deal on its style sheets for proper functioning. Having the body class be "tundra" is important, so don't change that.
The first table includes the banner, the date/time display, and the status line. The color surrounding the current status indicates the state that the WBUI is in. Green means that an operation succeeded, yellow indicates an in progress operation, and red indicates that a client request failed. The color is controlled by resetting the class of the oscarsStatus DOM node.
The next div section creates a global variable with the OSCARSState class. It is referenced simple by oscarsState in all the WBUI Javascript modules and HTML pages.
The next div section sets up the TabContainer and its initial Login/Logout tab (ContentPane). All HTML pages referenced by this and other tabs are loaded when the corresponding ContentPane is selected. Only the portion of the page for the current ContentPane changes when another tab (and HTML page) are selected. The whole page is not reloaded.
This is the reason that the back/forward browser buttons cannot be used; you will exit the application. A fix for this has been under investigation for awhile, but has not been successful yet. There are hooks in index.html to allow this, but they are not currently functional.
All the static HTML pages are contained in web/forms. Nothing has been done to protect the HTML pages normally requiring authorization from being viewed. They are non-functional without the appropriate server authorization.
The pages are named similarly to the tab/ContentPane that loads them. Most of the Dojo setup has already been done in index.html and does not need to be repeated in these pages. These pages are tightly coupled to the correspondingly named Javascript modules in web/oscars. An attempt was made to keep as much Javascript as possible out of the HTML pages and in the modules. Pages starting with reservation* deal with requests to the BSS. Pages starting with user* deal with the AAA. Functionality used by a number of pages is covered here; it would be too lengthy to cover them all in detail.
It is important to distinguish between two types of entities: HTML DOM nodes such as div and table, and Dojo widgets such as dijit.form.Button. Each Dojo widget is given an id unique across all HTML pages, since the id is global and allows it to be accessed anywhere in a script. A Dojo widget is retrieved using the dijit.byId(id) call, and allows Dojo-related methods to be called on it. Each Dojo widget is also accessible as a DOM node which can be used for Javascript DOM-related operations, accessed by dijit.byId(id).domNode or by dojo.byId(id).
Each non-widget DOM node that has an id (again global) assigned to it can be retrieved using dojo.byId(domId). Id's have only been assigned to such nodes if it is desired that its display status can be changed (visible or not), or that its innerHTML can be changed. The Dojo Book provides more information on ways of accessing nodes and widgets.
Note that dojox.layout.ContentPane is used to contain the HTML pages rather than dijit.layout.ContentPane (programmatic tab creation and the associated loading of an HTML page are covered in a Common tasks section below). The dojox version has more functionality, especially for being able to tie an event to the the initial page load. An example of this usage is in reservationCreate.html, in the script in its head section:
dojo.connect(dijit.byId("reservationCreatePane"), "onLoad", oscars.ReservationCreate.init);
reservationCreatePane is the id of the containing ContentPane for reservationCreate.html. oscars.ReservationCreate.init is called on the first page load, and only on the first page load. This method is located in the file web/oscars/ReservationCreate.js.
dojo.connect is used in a number of places to connect event handlers to events associated with a widget. Note that in this instance, where an HTML page is being loaded within a ContentPane, this usage of dojo.connect works, and dojo.addOnLoad doesn't.
In addition to the "onLoad" event, dojo.connect is also used with the "onkeypress" event in the HTML pages (for an example, see web/forms/reservations.html). The event handler in these cases is using the event's key code to know when "Enter" has been pressed, which is an alternative on some pages to having a button to submit a form. Note that in this case dojo.stopEvent must be called after form submission, or the page will stop working under Safari. More on events is covered in the Dojo Book section.
Another way that events can be associated with widgets, particularly button presses, is with "dojo/method". This is covered in the Dojo book in a concise way on a high-level overview page, which is useful in general in getting started. An example of its usage within OSCARS is in web/forms/reservationCreate.html. A script with this type is embedded within the declaration of a widget such as a button, and in this instance is tied to the button's "onClick" event.
All forms have the widget type dijit.form.Form. These are used because of the (currently) undocumented method "validate". There are a large number of Dojo TextBoxes in the HTML pages, primarily ValidationTextBox and one instance where NumberTextBox is used.
ValidationTextBox's have a number of useful attributes. One is a prompt message that appears when the user begins to type input; another is an invalid message that appears when a user has typed invalid input according to some regular expression, or when a user has not entered a required field. Unfortunately the former is activated when the focus enters rather than leaves the text box, so a warning triangle will appear until the regular expression is satisfied. NumberTextBox's allow constraints such as minimum and maximum values. An example is in web/forms/reservationCreate.html, for the bandwidth setting.
Before a form is submitted, the Form's validate method is called by the Javascript modules. If some of the fields are invalid, the form is not submitted, all invalid messages are displayed, and the cursor is set to the first invalid field.
Two types of grids (tables on steroids) are used, one in web/forms/reservations.html, and the other in web/forms/userList.html. In both these files, the table layout is set up in a script section in the top. The cells set up the table headings and the field names that the client expects in incoming data. Setting up the exact widths of each field appears to be necessary. Note that the reservations setup has two items in its inner array, to be able to handle subrows, in which text taking up two or more lines in the table is treated and styled as a unit by Dojo.
In userList.html, a static data store (dojo.data.ItemFileReadStore) is used which is destroyed and recreated programmatically (since it is static) on each user list refresh in web/oscars/userList.js from the servlet reply.
In reservations.html, a storeless model is used in which the data is formatted in a raw way by the servlet in exactly the way the grid expects. Only the dojox.Grid widget is set up in the HTML for that page. Note that this was necessary to be able to handle the more dynamic search setup for that page in a very simple way, as opposed to using the complex dojo.data.QueryReadStore. The storeless model could potentially be used for the user list as well, but the latter code is working.
More on how these two types of grids are filled by the servlets and handled by the modules is given in the following section. There is a great amount of material associated with grids, and you will need to refer to the Dojo documentation for the complete meaning of views, layouts, data stores, and models. The storeless model is not covered there, but in a Dojo forum thread (after the initial postings).
All Javascript modules are in the "oscars" namespace, and are located in web/oscars. Each registers themselves with Dojo with a dojo.provide line at the top of the module. dojo.require is called for each module on the main page to actually make their methods available.
The modules are in three groups. One contains methods having to do with date/time handling, display, and conversions (DigitalClock.js). Two (Form.js and Utils.js) contain methods used by all tab-related modules. The tab-related modules have names corresponding to their associated HTML file. Note that Dojo classes are not used. Not all methods are described for space reasons, but there are comments within the modules.
The tab-related modules (referred to just as modules below) use Ajax to communicate with the servlets. The Dojo Ajax calls wrapping XMLHttpRequest are dojo.xhrGet and dojo.xhrPost. In the current implementation, only dojo.xhrPost is used, and all the event handlers containing dojo.xhrPost calls are placed at the beginning of each module.
The following is an example call to dojo.xhrPost from web/oscars/ReservationCreate.js, from the createReservation method:
dojo.xhrPost({ url: 'servlet/CreateReservation', handleAs: "json-comment-filtered", load: oscars.ReservationCreate.handleReply, error: oscars.Form.handleError, form: dijit.byId("reservationCreateForm").domNode });
The "url" attribute contains the servlet to submit the form to. Recall that this mapping is set up in web/WEB-INF/web.xml. "handleAs" gives the MIME type that is expected for the reply. "load" gives the name of the method that will handle the JSON-encoded reply. Each of the modules has an associated handleReply method. "error" gives the name of the error handler that is encountered if an HTTP error code is returned, and "form" is the DOM id of the form that is being submitted.
The HTTP error handler used is common to all modules, and is contained in Form.js. It also contains the selectedChanged event handler, which is tied to the TabContainer's "selectChild" event on the main page. It's only function is to select one of these module's similarly named methods, based on the passed in ContentPane's (tab's) id.
Also in Form.js, resetStatus is a method called at the beginning of every module. The responseObject argument is the Javascript object (extracted from within comments by Dojo) returned in the JSON sent back from the servlet. Each servlet must set several properties of this object when it sends the JSON reply. These are an identifier referring to that servlet ("method"), whether there was an error or not ("success"), and a status message ("status"). The status line is reset to the status message, and the color of its outline changed by resetting the class of its enclosing table cell based on success or failure.
The next method in Form.js, applyParams, depends on a number of naming conventions, shared by the modules and the servlets, regarding the naming of returned properties and their values. These conventions are expedient rather than elegant, and could be extended with others. The name/value pairs returned are operation dependent.
Property names returned by a servlet must correspond to the id of a node or widget within a form, or they are ignored (as in the case of "status", etc. above). In some cases the suffix of the property name is used to determine what to do. In the case of "Replace", the property's value is used to replace the innerHTML of the corresponding element. "Display" changes the visibility of the corresponding DOM element, depending on whether the value is true or false. "TimeConvert" is a special case for converting that property's value from seconds to a date/time string, and replacing the innerHTML with that. Property names ending with "Checkboxes" are a special case to set up whether a checkbox should be modifiable, and which checkboxes to check. The default is to set the corresponding Dojo widget's value to the property value.
A successful servlet reply may result in the selection of a new tab. For example, if a user is added, oscars.UserAdd.handleReply will select the "User List" tab.
Servlets expect time-related parameters to be in seconds. createReservation.html and reservations.html have calendar-based dijit.form.DateTextBox's for the date, and ValidationTextBox's to constrain the time to be a valid HH:MM value, as well as date/time strings in the reservations grid on the latter page. There are a number of conversion methods in oscars.DigitalClock, called by these modules, that convert from these formats to seconds on the servlet request, and from seconds to widget values or date/time strings on the reply. Hidden form elements are used to hold the seconds values in several cases.
Grids are the most difficult widgets to use. The HTML side of their setup is covered in the section on HTML pages. The main problem was that the logical place to put grid initialization (in a handler for an "onLoad" event) resulted in no grid display. The situation was worse for the reservations grid (see oscars.Reservations.handleReply). Grid initialization for both the users list and the reservations list is handled the first time the corresponding tabSelected method in those modules is called; resetting attributes in the global oscarsState accomplishes that.
The event handler for row clicks is set up as part of grid initialization. If a row in the reservations grid on the "Reservations" tab is clicked on, the "Reservation Details" tab is selected to display the result of the QueryReservation servlet call. If a row in the users grid on the "User List" tab is clicked on, the "User Profile" tab is selected to display the results of the UserQuery servlet call.
If the user grid needs to be refreshed, as a result of a user add or a user deletion, a new ItemFileReadStore is created, which is tied to a call to the UserList servlet. A new model is set up, and the grid's refresh method is used to bring the data over and format it. See the outputUsers method in the UserList servlet for how the data store is populated. Note that the field names there are those given in the view setup at the top of userList.html.
The reservations grid uses a storeless data model, as was mentioned in the section on HTML pages. There are no field names involved in the ListReservations servlet's outputReservations method. Everything depends on adding the entries in the same order as in the fields in the view setup on the HTML page, in a two-dimensional list matching the view.
With the user list data store, a map is returned, and Dojo handles more of the work. With the reservations list, the servlet is explicitly called on a form submit, as opposed to the call being implicit with a grid refresh on the ItemFileReadStore. Calling model.setData in oscars.Reservations.handleReply with the servlet response refreshes the reservations grid. Note that the fields with seconds are converted to date/time strings before setData is called.
Additional information about the modules is described in the section on common tasks.
The one style sheet specifically associated with the WBUI is located in web/css/main.css. It contains miscellaneous rules associated with OSCARS; most of the style rules are in the three Dojo style files listed at the top of the main page.
The ".required" and ".warning" rules are used to control the color outlining DOM elements such as table cells that are required, and that require special notice, respectively. ".success", ".failure", and ".inprocess" are rules controlling the color outlining the table cell with the status line.
Specific rules overriding Dojo rules are at the bottom of this file. Their comments indicate their function. The last four rules make alternating rows in grids stand out more clearly, and make long rows fit more easily on the page.
If you need to add functionality not covered by a widget currently used within the interface you will need to look at the Dijit book section, which covers all the widgets grouped by function, to find an appropriate one. The documentation for each widget and any associated events is fairly complete, but in some cases you may have to look at the tests or the source for the proper usage. Grids are covered under this book section as well.
After Dojo has been auto-installed, most widget tests can be found in web/lib/dojo/dijit/tests. They contain working examples, but have the drawback that most are single page. The source for the widgets is in the next directory up. The exceptions are any dojo.data or dojox.grid related modules, which are under web/lib/dojo/dojo and web/lib/dojo/dojox.
After a widget has been found and used, it will need to be added to the list of dojo.require's on the main page, for example dojo.require("dijit.form.Button"), or else the tab where that widget is added will not display.
Adding a new tab involves creating a new HTML page under web/forms, adding code to web/oscars/UserLogin.js and UserLogout.js to programmatically create and destroy the tab, writing a new Javascript module under web/oscars to handle events and communication with the server, writing a new servlet under src/net/es/oscars/servlets, and setting up new information in web/WEB-INF/web.xml so that Tomcat can handle the communication between client and server. Therefore it's fairly involved.
In general, the current code on both the client and server side can serve as models for all of this. The conventions described in the section on Javascript modules should be followed in setting up those modules and communication between the client and server.
Tabs other than Login/Logout are created programmatically in web/oscars/UserLogin.js in the handleReply method after successful user authentication. As an example, the following code in handleReply creates the ContentPane for the User Profile tab, and loads the corresponding web/forms/userProfile.html file:
var userProfilePane = new dojox.layout.ContentPane( {title:'User Profile', id: 'userProfilePane'}, dojo.doc.createElement('div')); userProfilePane.setHref("forms/userProfile.html"); mainTabContainer.addChild(userProfilePane, 3); userProfilePane.startup();
The setHref call loads the corresponding HTML file. The ContentPane is then added to the main TabContainer. Note that the integer determines the tab's place in the tab list at the top of the TabContainer. Don't forget the startup call, which completes the setup.
Any tab created programmatically needs to be closed in the handleReply method of UserLogout.js. Otherwise, the next login will fail because a widget with that id (in this case "userProfilePane") already exists. The following code does this for the widget with that id:
if (dijit.byId("userProfilePane") != null) { mainTabContainer.closeChild(dijit.byId("userProfilePane")); }
Two sets of elements need to be added for any new servlet in web/WEB-INF/web.xml. One is a servlet element to map between a servlet name and a servlet class, and the other is a servlet-mapping element that maps between the servlet name and a relative URL.
Each HTML page may contain different events that result in calls to multiple servlets, so there is not a one-to-one mapping. Adding new servlet calls from an existing page requires a subset of the tasks discussed in the previous section, i.e. no new HTML page, no additional tabs, and no new module is required. A widget setting up a new event needs to be added to the HTML page, a new event handler added to the corresponding module, a new servlet written, and web.xml needs to be modified.
There are several tools and sources of information that will make developing new forms and servlets easier.
The use of Firebug and Firebug Lite is covered in a tutorial by the Dojo developers.
Usage of the Firebug debugger statement is somewhat temperamental. You can insert it at any point in the Javascript code, for example web/oscars/Form.js. You won't see the code where it stops when you click on the Script tab (you'll see the code for dojo.js), and if you're not careful where you move the mouse, you're back to the HTML code inspector. However, if you stay in the Script tab, on the right side of the window you can inspect any variables that exists where the breakpoint is set, and continue to the next breakpoint and inspect variables there.
Javascript syntax errors will result in cryptic messages in Firebug such as "oscars.Form has no properties" if there is a syntax error in web/oscars/Form.js, with no clue as to where the syntax error is. That is where JSLint and Rhino come in. JSLint is a Javascript syntax checker that can check HTML and Javascript files for errors and bad usage. web/lib/jslint.js is included with the OSCARS distribution because there is no license.
There are still a lot of warning messages for files like web/oscars/Form.js, but none are fatal. You will need to temporarily remove embedded dojo/method scripts in the HTML files in forms, because JSLint thinks that they are illegal locations for Javascript. That's still worthwhile doing, because I found other errors once I worked around that restriction. JSLint will flag any Dojo regular expressions used, but they are non-fatal.
JSLint by itself requires pasting your code into its Web page, which is very cumbersome. With the use of Rhino, which is written in Java, you can use JSLint on the command-line. We cannot redistribute its jar because of licensing issues. You can download and unzip it, and place js.jar in lib in the main directory. After that you can run the syntaxChecker.sh script in web to check for syntax errors in any of the HTML or Javascript files, for example "./syntaxChecker.sh oscars/Form.js".
The Dojo forums contain user questions and replies from either more experienced users or from developers. This interface could not have been developed without it. You will need to set up a login to be able to post questions. Note that this system is not perfect and you may need to do some digging (sorting by date would be nice, since Dojo has gone through a major revision in the last year). Some material (especially anything with 0.4) is out of date.
You will have a much greater chance of a reply if you can condense the problem you are having into a small amount of sample code that someone else can use to reproduce the problem. Doing so has been more of a problem as the interface has grown. As always in such a system, search first before posting.