Ultra-Lightweight Reusable Presentation Components With JSP Romel Rivera, CTO, eKnowlogie First Posted October 12, 2004 Last Modified, November 24, 2005 Copyright © 2005 eKnowlogie, Inc. All rights reserved.
Index 0. Abstract 1. Introduction 1.1. Architectural Context 1.2. Underlying Support 1.3. Presentation Functions 2. Rapid Presentation Prototyping 3. The Framework 3.1. Rendering 3.2. Control 4. Customization 4.1. Core Preferences 4.2. Cascading Preference Selection 4.3. Preference Registration Examples 4.4. Validation Messages 4.5. Composite Integrity Validation 4.6. Subclassing Composite UIs and Inter-Field Dynamic Relationships 4.7. Interchangeable JSP Rendering Components 5. Spanning Activities Over Multiple Pages 6. List Processing 7. UI Factories and User Privileges 8. Creating New Preferences 9. Aliorsum and Hibernate 10. Benefits 10.1. Residual JSP Guidelines 11. Implementation 12. References
Aliorsum® is a JSP-based ultra-light framework for interactive object model manipulation, designed to provide web-based, automated component-oriented reusable presentation support to the components in the application domain object model. It hides HTTP input manipulation and request-oriented fractured processing in favor of domain object manipulation in conceptual task-oriented presentation activities. The framework's effectiveness for rapid presentation development stems from its customizability to support high-level domain model representation instead of detached extensibility of low-level presentation widgetry.
The framework is ultra-lightweight and only uses standard Java and JSP; the framework was written in a few hundred lines of Java. Input and rendering is done using a sealed component-oriented contract with JSP. Validation is model-oriented and not HTTP request-oriented. Presentation activities can exist independently of and across multiple page requests. The presentation can be customized directly from the domain model characteristics or business presentation rules. This framework is designed to complement applications supporting a rich, full OO domain model, isolated from cross-cutting concerns and with transparent object/relational persistence such as that provided by Hibernate. This framework does not need to be used exclusively, it can be used selectively and in coexistence with regular JSP. Aliorsum can be downloaded free of charge under the GNU General Public License.
Presentation frameworks, such as Swing, JSF, and Tapestry, provide an exclusive emphasis on the bottom-up construction of widgetry. Their formidable complexity and steep learning curve is never mitigated by the provision of a relationship to the domain model they are meant to represent. This not only makes the construction of user interfaces far more time-consuming and much more difficult to understand, maintain, and reuse, but also leaves architects and developers orphan from a mechanism to carry their OO models all the way to the eyes and hands of their users.
An object-oriented presentation is the representation of its application's domain object model. Therefore we need a full domain object model with unintrusive persistence (i.e. hidden behind the model). For object to relational persistence, these are two alternatives:
Because this framework is built on top of JSP, we offer a decidedly incomplete list of JSP's advantages and disadvantages.
JSP Advantages
JSP Disadvantages
2 Rapid Presentation Prototyping
Consider as an example, the creation of a purchase order during checkout in a shopping cart application. Consider that the Java domain model for the order is a class Order which includes fields of type BillingInfo and ShippingInfo. BillingInfo contains the dollar amounts for the net price of products selected and carried over from the shopping cart, plus sales taxes, shipping and handling charges, and total amount. BillingInfo also contains a credit card field ot type CreditCard which in turn contains a billing address of type Address. ShippingInfo contains a shipping address also of type Address. A class Address is shown below:
public class Address {
public String firstName; public String lastName; public String addressLine1; public String addressLine2; public String city; public String state; public int zip; public Phone phone; public Phone fax; public String email;
}
Example 1. Class Address
The following example shows the JSP page "checkout.jsp" for the checkout process in the shopping cart. All that needs to be written is this page below.
<%@ page import = "com.eknowlogie.aliorsum.*, com.acme.shoppingcart.model.*" %> <%
StoreVisit visit = StoreVisit. getStoreVisit (session);
if (UIActivity. isFirstTime (pageContext)) { visit. initializeOrder (); // Initializes net price, taxes, totals UIActivity. begin (pageContext); UIActivity. setControl (pageContext, "accept_order.jsp", "catalog.jsp"); } else if (UIActivity. validateAndForward (pageContext)) return;
CompositeUI. presetUIRendering (visit. getOrder (), pageContext);
%>
<html>
<head/>
<body>
<form method = "post">
<h3>Please enter billing and shipping information:</h3>
<jsp:include page = "./aliorsum/UIRenderer.jsp" />
<input type = "submit" name = "submit" value = "Submit Order" />
<input type = "submit" name = "cancel" value = "Cancel Order" /> <input type = "hidden" name = "formSubmitted" value = "yes" />
</form>
</body>
</html>
Example 2. "checkout.jsp"
The JSP page above produces a form validation loop to input the customer order with the structure provided in the domain model. A snapshot of the user-interface rendition for that validation loop is shown below:
Example 3. Prototyped Order Form
Notice that, as shown in class Address above, the domain model classes are unaware and independent of the existence of a (JSP) user-presentation layer for these classes. There are no requirements imposed in the construction of these classes in order to support this presentation layer. Notice also that both valid and invalid inputs are preserved during the validation loops, as it is important that information is never lost through these iterations, and that users can make corrections to invalid input they have previously entered. Validation messages can be used to provide explanations or instructions before user input, and validation errors after user input.
A Java UI class provides presentation management for the creation, input, validation, formatting and rendering of a domain model object called its recipient (e.g. the Order instance above). An elementary UI manages a recipient (including all primitive Java types) as one single indivisible value. A composite UI manages a recipient's presentation by managing the presentation of its constituent fields. A JSP renderer is a JSP fragment (an include file or a jsp:include page) contractually sealed as a component to provide the visual presentation of a model object in Html. A UI activity is a container which holds all the UIs in use throughout a given presentation activity, and can provide validation control flow throughout the activity. The life-span of a presentation activity is independent of a page or request and can spread arbitrarily over multiple pages or requests. Contrary to other frameworks like Struts, this provides continuity as partially-filled or invalid inputs are preserved across multiple page sequences. Preferences are properties used as one way to customize the behavior of the user-interface. The UI factory is the place where preferences are registered and selected, and where elementary and composite UIs are selected and created to service specific recipients.
A presentation activity is bracketed by calls to begin and end the use of the activity. Similarly, the rendering of a composite object in JSP is bracketed by calls to begin and end the rendering for that object. We can also preset the rendition of an object ahead of its begin rendition in order to customize the rendition to specific parameters. In the JSP checkout.jsp example above, "./aliorsum/UIRenderer.jsp" will perform the bracketed (recursive) rendition of an object, which is preset ahead of time to indicate that the object to be rendered is "visit. getOrder ()". Adding a boolean parameter readOnly = true to the preset rendition call in the example below, would disable input for the entire "visit. getOrder ()" object structure and simply present it for display. The page "./aliorsum/UIRenderer.jsp" is a standard Aliorsum component for generic object rendering and is shown below:
<%@ page import = "java.util.Iterator, java.lang.reflect.Field, com.eknowlogie.aliorsum.*" %>
<html>
<body>
<%
{
CompositeUI ui = CompositeUI. beginUIRendering (pageContext);
String prefix = ui. getNamePrefix ();
%>
<%= ui. renderValidation () %>
<table border = "2" cellpadding = "10" valign = "top">
<%
Iterator it = ui. childrenUIs ();
while (it. hasNext ()) {
UI childUI = (UI) it. next (); if (childUI instanceof ElementaryUI) {
%>
<%= ((ElementaryUI) childUI). renderCols () %>
<% } else {
%>
<tr>
<td colspan = "2">
<%= childUI. renderTitle () %> <br/><br/>
<table width = "100%" valign = "top">
<tr>
<td> </td>
<td>
<jsp:include page = "<%= ((CompositeUI) childUI). getRenderer () %>" />
</td>
</tr>
</table>
</td>
</tr>
<% }
}
%>
</table>
<input type = "hidden" name = "<%= prefix %>formSubmitted" value = "yes"> <% ui. endUIRendering (); } %> </body> </html>
Example 4. Aliorsum's UIRenderer.jsp
A name prefix is used to uniquely qualify form field names to avoid duplications during the activity. That is why two renditions of class Address can be used to produce the billing and shipping addresses in our example. The prefix is also used to generate a unique formSubmitted field used to signal submission of each composite recipient's input (required by Aliorsum).
Notice that a regular include file with the include directive can be used instead of jsp:includes as the insertion mechanism for JSP renderers, except for situations where a statically-referenced include file is recursive and might include itself. Because regular include files are expanded before the page is compiled, recursive includes would generate infinite loops during include expansion. Notice that when rendering the composite fields of an object, UIRenderer.jsp could recursively include itself as the renderer. Instead, UIRenderer.jsp aks the field's composite UI to provide the name of a renderer (which normally defaults to UIRenderer.jsp).
There are several ways to render an elementary field inside a JSP composite renderer. One is to ask its elementary UI to render it for us, which will produce the routine Html for rendering the field. In the case above, the rendering is produced with "renderCols", one of the elementary UI's rendering methods, which renders the title, validation and value of the field as a row of two columns. Elementary renderers are convenient because they make use of preference settings (such as read-only) in their rendering. Another way is for us to render the field explicitly inside a JSP renderer, in which case, the elementary UI can provide rendering information for us such as field title, field name, validation or explanatory messages and formatted (valid or invalid) output of the field's present value. The example below shows the explicit rendering of field "firstName" in class Address as a row with two columns.
<% ElementaryUI eui = ui. getElementaryUI ("firstName"); %> <tr>
<td>
<%= eui. renderTitle () + ": " + eui. renderValidation () %>
</td>
<td> <%
if (eui. isReadOnly ()) {
%> <%= eui. format () %>
<% }
else {
%>
<input type = "text"
name = "<%= eui. getQualifiedName () %>"
value = "<%= eui. format () %>"
/>
<% } %>
</td> </tr>
Example 5. Explicit
Elementary Field Rendering in JSP
The Java sequence in the "checkout.jsp" example shown earlier makes use of a very simple mechanism for controlling the activity loop, which assumes standard submission buttons "submit" and "cancel". Method "setControl" was used to set the success and failure pages for this activity, and "validateAndForward" provided the validation of input data for objects in the activity, as well as the resulting control mechanisms for page forwarding. Page "accept_order.jsp" would make the order persistent (by invoking "visit. placeOrder ()"), and if successful, display the order number and a message thanking the customer. Though not an MVC architecture, we can convene, as in checkout.jsp, to have JSP pages relinquish control to the activity and obtain comparable MVC separation but within the cohesion and extensibility of self-contained Java code and without XML control descriptors replicating Java control flow semantics.
Alternatively, if we wanted to perform the closing-up operations ourselves after a successful activity, we could rewrite using UI activity's "isLastTime" method or simply rewrite to retain full validation control as shown below. Notice that the control operations in the UI activity do not invoke the activity's begin and end brackets, as they need to always be supplied explicitly (though the end bracket is optional).
<%
StoreVisit visit = StoreVisit. getStoreVisit (session);
if (request. getParameter ("submit") != null) {
if (UIActivity. validate (pageContext)) {
visit. placeOrder (); // Makes the valid order persistent with Hibernate
UIActivity. end (pageContext);
pageContext. forward ("order_accepted.jsp"); return;
}
}
else if (request. getParameter ("cancel") != null) {
UIActivity. end (pageContext);
pageContext. forward ("catalog.jsp"); return;
}
else { visit. initializeOrder (); // Initializes net price, taxes, totals UIActivity. begin (pageContext); }
CompositeUI. presetUIRendering (visit. getOrder (), pageContext);
%>
Example
6. Explicit Validation Control
The fully-reflective generic rendering page UIRenderer.jsp is ideal for rapid prototyping, for very quickly producing a presentation layer from a domain model. Aliorsum offers full JSP flexibility and customizability to produce presentation layers. In Aliorsum, there are three organized mechanisms for customizing user interfaces, which may be used in any combination:
Polycustomization refers to the versatility with which these different customization mechanisms can be applied and combined.
Component Triad Interchangeability. In Aliorsum, there are three types of components that are used to produce a presentation: the model objects or recipients, the UIs which manage their presentation, and the JSP renderers which render their presentation. Through polycustomization, the components in each category can be specified to be interchangeable with the components in the other categories: multiple JSP renderers can be used with multiple composite UIs to support multiple application model recipients.
Even though Aliorsum relies on static factory methods, there are no singletons in the Aliorsum architecture. UI subclasses are chosen and created from preference settings, UI activity and UI factory subclasses can be chosen by the application or injected into an application through inversion of control containers, such as Spring.
A preference for a composite object is inheritable if it can be propagated to its fields. A read-only display preference is inheritable; if shipping address of type Address is read-only, it follows that its zip code is also read-only (unless explicitly stated otherwise). A title preference is not inheritable; a title "Shipping Address for All Purchases" for a shipping address cannot be used to label its zip code field. An inherited preference value can be overriden by a nested field with a preference value of its own. Arbitrary preferences can be specified in Aliorsum. The core preferences provided with Aliorsum include the following:
Consider this example of polycustomization for the selection of titles for given objects: while having overriding titles specified as field preferences may be the most modular and versatile approach, the developer could alternatively supply field titles to rendering JSP's directly from a dedicated composite UI subclass for that recipient, or simply by hand-coding it into a dedicated JSP rendering page for that recipient. We say that a field title is polycustomizable.
Cascading Preference Selection
Preferences can be specified or registered at three different levels of generality:
Preferences can also be specified for different life-times:
The preference classifications listed above are used by a cascading preference selection mechanism where a preference is first looked up in the activity preferences, then in the session preferences, by going through all levels of generality for each, cascading from specific to general.
The search for class-specific preferences is extended as needed to the superclasses (super class preference search). A class-specific search may be repeated as needed for the classes of the enclosing objects being rendered (contextual preference search), provided such preference is inheritable.
UI factories are used for preference selection. Activities have their own local factories with the session factory as their subordinate factory.
Preference Registration Examples
Consider an application domain object model to have a minimum of three layers of subclassification, e.g. the generic layer for primitive (Java) data types, the general domain layer (which for a shopping cart would correspond to a general business layer with components such as Address and CreditCard), and an application domain layer (which for a shopping cart would correspond to catalogs, products, shopping carts, orders and so on). We can provide corresponding UI preference registrations for these layers, to be invoked from the bottom-up during UI factory initialization so that higher-layer preferences can overwrite lower layers. Provided defaults for the core preferences are initialized directly by a UI factory.
public class Prefs { public static void registerDefaults (UIFactory uif) {
uif. defaultPrefs (). put (ACCESS, Access. READ_AND_WRITE);
uif. defaultPrefs (). put (IS_REQUIRED, new Boolean (true));
uif. defaultPrefs (). put (COMPOSITE_UI_CLASS, CompositeUI. class);
uif. defaultPrefs (). put (ELEMENTARY_UI_CLASS, ElementaryTextUI. class);
uif. defaultPrefs (). put (RENDERER, "UIRenderer.jsp"); ...
} }
Example 6. Default Preference Registration
The bottom primitive preference registration is also provided and initialized directly by a UI factory, and as of the example above would include the following:
public class PrimitivePrefsRegistrator {
public static void register (UIFactory uif) { uif. classPrefs (). register (int. class, Prefs. ELEMENTARY_UI_CLASS, IntegerUI. class); uif. classPrefs (). register (Integer. class, Prefs. ELEMENTARY_UI_CLASS, IntegerUI. class); uif. classPrefs (). register (Date. class, Prefs. ELEMENTARY_UI_CLASS, DateUI. class); uif. classPrefs (). register (DecimalCurrency. class, Prefs. ELEMENTARY_UI_CLASS, DecimalCurrencyUI. class); uif. classPrefs (). register (DoubleCurrency. class, Prefs. ELEMENTARY_UI_CLASS, DoubleCurrencyUI. class); uif. classPrefs (). register (DecimalCurrency. class, Prefs. ACCESS, Access. READ_ONLY); uif. classPrefs (). register (DoubleCurrency. class, Prefs. ACCESS, Access. READ_ONLY); ... } }
Example 7. Preference Registration for Primitive Types
Notice how currency amounts are considered to be primarily read-only fields. This can be reversed for all fields or for selected fields in the domain preference registrations. Consider now the following domain customizations, which are needed to support our original order processing example.
public class BusinessUIPrefsRegistrator {
public static void register (UIFactory uif) { uif. fieldPrefs (). register ("addressLine2", Address. class, Prefs. IS_REQUIRED, false); uif. fieldPrefs (). register ("fax", Address. class, Prefs. IS_REQUIRED, false); uif. fieldPrefs (). register ("expirationDate", Address. class, Prefs. ELEMENTARY_UI_CLASS, FutureDateUI. class); uif. classPrefs (). register (Address. class, Prefs. COMPOSITE_UI_CLASS, "AddressUIRenderer.jsp"); } }
public class ShoppingCartUIPrefsRegistrator {
public static void register (UIFactory uif) { uif. fieldPrefs (). register ("creditCard", BillingInfo. class, Prefs. TITLE, "Credit Card (Visa or MasterCard Only)"); uif. fieldPrefs (). register ("orderId", Order. class, Prefs. ACCESS, Access. HIDDEN); uif. fieldPrefs (). register ("otherServices", BillingInfo. class, Prefs. ACCESS, Access. READ_ONLY); uif. fieldPrefs (). register ("otherServices", BillingInfo. class, Prefs. IS_REQUIRED, false); } }
Example 8. Preference Registration for the Business and Application Domains
We have indicated that field orderId in class Order should be hidden, as we will provide that to the customer only after the order has been successfully placed, and that the second address line and the fax number should be optional fields. The otherServices field is both read-only and optional, since it is an explanation of all the other services the user has selected and it will be hidden if no services have been selected. This programmatic interface for preference registration keeps the application developers and designers in direct relationship with the Java architecture created for the application. Programmatic intelligence can be used to determine the credit cards currently accepted by a given online store without requiring a level of indirection in having to post such preferences through XML.
We have also indicated that another elementary UI, called FutureDateUI will be used to process the Date class instead of DateUI but only for field expirationDate in class Address. The implementation of FutureDateUI would be in the spirit shown below:
public class FutureDateUI extends DateUI {
public Object parse (String inputValue) {
Date date = (Date) super. parse (inputValue);
if (date != null) {
if (Dates. greaterThan (date, Dates. today ())) {
return date;
} else addValidationMsg ("Past date is invalid.");
}
return null;
}
}
Example 9. FutureDateUI
The new parse() method above will be used by the DateUI's validation methods we did not override. The more object-oriented, the more subclassed our model is, the more UI functionality we can obtain automatically, and this is true for elementary classes as well, such as Date, Phone and Zip. For example, by creating and elementary Email class, we can properly validate, discard undesirable addresses, subclass its elementary UI to insure that employees enter their company email instead of their personal one, and render email fields as mailto Html tags when displayed as read-only.
Notice that multiple validation messages can be posted at each UI since the last input was received. Validation messages can be used to provide explanations or instructions before user input, and validation errors after user input. Validation messages may also be displayed in the form of footnotes, so that explanatory messages are displayed at one single location in the page. Validation conditions can also be used to control rendering, for example, by displaying only invalid fields during validation loops, in order to better isolate for the user the locations for the needed corrections.
Composite Integrity Validation
Input validation is a process best separated into syntactic validation which can be accomplished through parsing in a domain-independent way, and semantic or integrity validation which is an object-oriented, domain-specific activity necessary in order for objects to self-verify their integrity and if possible, make corrective adjustments. Integrity validation becomes more delicate for composite objects as it requires the analysis of inter-field relationships. For example, class Order can update its fields during composite integrity validation by providing an explanation of other services in the otherServices field if any additional services were selected regarding shipping or perhaps product assembly. It can also update the total dollar amount in the order to reflect services and shipping options selected by the user during order input. Remaining incorrect inconsistencies, such as perhaps the condition that "gift wrapping cannot be shipped via overnight service" would be identified at this time.
Composite (and elementary) UI's also need to report these integrity discrepancies to the user during interactive input. Composite UI's in particular can be subclassed to override the validateComposite() method and invoke validating operations for their specific recipients (e.g. Order). Even better, domain model classes can optionally implement an interface used automatically by generic composite UI's waving the need for subclassing.
public interface Validator { public boolean validate () throws IllegalStateException; }
Example 10. The Validator Interface
Integrity validation operations should already be part of these domain classes, and optionally publicized through the interface above. The enquiring UI would then receive the validation message through the exception raised, and post it for that composite recipient. Notice that this exception can be extended to support additional information exchange, in case we wanted to identify specific affected fields, etc., so that perhaps a message could be posted at the group-level and at the specific affected fields as well. A validation message posted only at the group-level, forces the user himself or herself to repeat the process of identifying the affected fields.
Subclassing Composite UIs and Dynamic Inter-Field Relationships
Composite UI's are used as filters for the selection and application of preferences. Composite UIs will transfer the request for preferences on to the activity's factory. As such, they can also override that process through logic conditions that cannot be posted as invariant preferences in the factory. Subclassed composite UIs typically can apply dynamic inter-field relationships in order to determine the value of these preferences. For example, if the "Wrapped As Gift" checkbox was selected, then fields for wrapping paper style, gift text, and color are required, otherwise they must be left blank. Composite UI subclasses and factory preferences complement each other and provide what the other cannot.
Interchangeable JSP Rendering Components
Aliorsum's "polycustomizability" applies to the selection of JSP rendering components, as we want flexibility in the selection of appropiate JSP renderers. For example, they can be selected invariantly through factory preferences or dynamically through composite UI subclasses. We may want to provide a new, dedicated JSP rendering component for the display of addresses, so that the city, state and zip appear all in one single line. We can do this through a factory preference by providing a JSP renderer for fields or objects of type Address, as shown in class BusinessUIPrefsRegistrator earlier. We can also choose a renderer for a given field dynamically through a composite UI subclass, which, for example, could determine the estimated length of the field's subfields in order to decide to use a vertical or horizontal tabular renderer. Aliorsum views JSP renderers as reusable, mix and match, interchangeable building blocks, where multiple alternative renderers are easily supported. An object structure "order.creditCard.billingAddress" might be using dedicated renderers for each object, or a generic renderer for order, a dedicated renderer for creditCard, and the same generic renderer again for billingAddress.
5. Spanning Activities Over Multiple Pages
We now rewrite our original checkout.jsp example so as to extend the checkout presentation activity over a sequence of two pages. The first page, checkout_billing.jsp will process the billing information, while the second page, checkout_shipping.jsp will proccess shipping. The example below shows the new checkout_billing.jsp.
<%@ page language = "java" import = "com.eknowlogie.aliorsum.*, com.acme.shoppingcart.model.*" %> <%
StoreVisit visit = StoreVisit. getStoreVisit (session);
if (UIActivity. isFirstTime (pageContext)) {
visit. initializeOrder (); // Initializes net price, taxes, totals UIActivity. begin (pageContext); UIActivity. fieldPrefs (). register ("shippingInfo", Order. class, Prefs. ACCESS, Access. HIDDEN); UIActivity. setControl (pageContext, "accept_order.jsp", "catalog.jsp",
new String [] {"checkout_billing.jsp", "checkout_shipping.jsp"});
}
else if (UIActivity. validateAndForward (pageContext)) return;
CompositeUI ui = CompositeUI. presetUIRendering (visit. getOrder (), pageContext);
%>
<html>
<head/>
<body>
<form method = "post">
<h3>Please enter billing and shipping information:</h3>
<jsp:include page = "./aliorsum/UIRenderer.jsp" />
<input type = "submit" name = "next" value = "Next" />
<input type = "submit" name = "cancel" value = "Cancel Order" /> <input type = "hidden" name = "formSubmitted" value = "yes" />
</form>
</body>
</html>
Example 11. checkout_billing.jsp
Only three lines changed in checkout_billing.jsp from the original checkout.jsp, as follows:
We now present checkout_shipping.jsp, the second and final page in the sequence.
<%@ page language = "java" import = "com.eknowlogie.aliorsum.*, com.acme.shoppingcart.model.*" %> <%
StoreVisit visit = StoreVisit. getStoreVisit (session);
Order order = visit. getOrder ();
if (UIActivation. validateAndForward (pageContext)) return;
else if (order. shippingInfo == null) {
order. shippingInfo = new ShippingInfo ();
order. shippingInfo. shippingAddress = (Address) order. billingInfo. creditCard. billingAddress. clone ();
}
CompositeUI. presetUIRendering (order. shippingInfo, pageContext);
%>
<html>
<head/>
<body>
<form method = "post">
<h3>Please enter billing and shipping information:</h3>
<jsp:include page = "./aliorsum/UIRenderer.jsp" />
<input type = "submit" name = "previous" value = "Previous" /> <input type = "submit" name = "submit" value = "Submit Order" />
<input type = "submit" name = "cancel" value = "Cancel Order" /> <input type = "hidden" name = "formSubmitted" value = "yes" />
</form>
</body>
</html>
Example 12. checkout_shipping.jsp
Page checkout_shipping.jsp above initializes the shipping address with a clone of the billing address in order to contemplate the most likely scenario where the billing and shipping addresses are the same and save the user from re-typing the same address twice.
The JSP renderer we used in the previous examples displays an object structure hierarchically, by using nested Html tables to represent each composite field or element in a list. There is another renderer in this framework that displays an object structure horizontally as a single flat table. The field titles are used as column headers and all the field values in the structure are displayed in a single row. If the object being rendered is a list, then the table would contain one row for each element in the list. These standard renderers provide a capability for the generic manipulation of lists with elements of any type, where list elements can be edited, deleted and added. Additional presentation support can be added for sorting by columns and query-based selection of columns and rows. The following table below shows a shopping cart with the list of all products selected for purchasing. A hierarchical table header renderer was used, to show the objects' hierarchical structure. The standard table renderer was used but customized to include the display of fields which are not part of the selected product's object structure, such as the price for each line item, subtotal, sales tax and total. An elementary CounterUI was assigned to the quantity selected which provides convenience buttons for changing the quantity.
acme.com | |||||||
---|---|---|---|---|---|---|---|
Shopping Cart Contents | |||||||
Product | Quantity Selected | Price | |||||
Name | Description | Category | Unit Price | ||||
Name | |||||||
Sony Portable CD Player | Anti-shock, water-proof, with programable functions | Electronics |
$59.99 |
Cannot order any more units of this item. There are only 5 units in stock. |
$59.99 | ||
Nokia Cell Phone | Intercom functionality, built-in digital camera | Electronics |
$149.99 |
|
$299.98 | ||
GE Toaster | With variable timer, for two slices | Appliances |
$19.99 |
|
$19.99 | ||
Subtotal : | $379.96 | ||||||
Sales Tax : | $30.40 | ||||||
Total : | $410.36 |
Example 13. Shopping Cart Contents
7 UI Factories and User Privileges
UI factories need to be instantiatable in order to support different user access privileges, information requirements, perspectives and preferences. For example, users from the accounting department need to see the salaries of employees but not modify them, while HR users both need to see them and modify them, while users from other departments are not allowed access to these salaries. This is a reason why the UI factory could not be a singleton.
Typically, a UI factory can be instantiated at the beginning of a user session, but if the use for specific UI factory configurations can be anticipated, then they can be created ahead of time, cached and shared among similar users. During UI factory instantiation, preferences are registered into the factory. Preference registration could be classified and grouped in registration classes for each privilege and security level so that these groups can be combined during factory instantiation according to the given security levels and privileges of a specific user. Though activities are typically created and discarded implicitly, activities and their local factory preferences can also be pre-created, pre-configured and reused throughout a session. An inversion of control container like Spring could also be used to inject a specific factory instantiation into a user session.
At any rate, the goal must be to prevent the explicit conditional testing for user privileges from permeating JSP page construction, which only adds unnecessary complexity to these pages. While different user privileges and perspectives require different functionality and thus different presentation activities, with this framework the need for explicit dedicated code, both on the presentation and server sides, can be minimized.
Even though Aliorsum supports the creation of arbitrary preferences, these preferences are typed and must always be grouped and made available to developers through theme preference classes, such as formatting and security preferences, in a manner similar to how the Aliorsum core preferences are made available. Creating and accessing properties as anonymous typeless textual strings, though a pervasive practice, circumvents Java encapsulation, referencial and type-checking mechanisms already awarded by the language, and exercise critical influence in the maintainability of source code. By using theme preference classes, these preferences can be given a unique global Java name which allows reference localization throughout the application, and type-checking of assigned values. Javadoc documentation can then be provided as the centralized repository where the nature and purpose of these preferences is described. These classes are also used to register the default values for these preferences. Under Java 5.0, all multi-valued preference flags should be declared as enumeration types to further support strong typing through convenience methods for their registration and access. Having all theme preferences situated in their own class also makes it easy to later perform optimized registration and searching.
Aliorsum and Hibernate are equivalent technologies; they both are transparent automated frameworks to map full, rich domain object models to alternate representations. As such, they bring a new, unified, harmonic, cohesive theme to architecture design. This is an outline of their similarities:
Potential enhancements for Aliorsum include:
The Aliorsum implementation does not require installation or set-up. To use this framework, copy the sources into your compilation paths and begin using it in your application. The following link, The Aliorsum API, provides the Javadoc API documentation for public and protected members, to exemplify how it may be used by client presentations. The source code can also be downloaded free of charge under the GNU General Public License: Download Aliorsum Sources. The author can be contacted at the following email address: info AT eknowlogie.com.
[Bauer04] Christian Bauer, Gavin King.Hibernate in Action. Manning, June 2004, 400 pages.
[Bergsten04] Hans Bergsten. Java Server Faces. O'Reilly, April 2004, 606 pages.
[Lewis04] Howard M. Lewis Ship. Tapestry in Action. Manning, March 2004, 580 pages.
[Loy02] Marc Loy, Robert Eckstein, Dave Wood, James Elliott, Brian Cole. Java Swing, 2nd Edition. O'Reilly, November 2002, 1278 pages.