Some reader favorites:
EJB fundamentals and session beans
Create a scrollable virtual desktop in Swing
More action with Struts 2
In a recent review of Struts 2 in Action, JW Blogger Oleg Mikheev notes that Struts 2 is "just a collection of extensions built upon WebWork, which is ultimately
the right thing to learn before starting a Struts 2 project." While Struts 2 has some architectural flaws, Oleg calls WebWork
well-designed, well-tested, and reliable. What are your experiences using Struts 2 and WebWork?
Also see "Hello World the WebWork way," a JavaWorld excerpt from WebWork in Action, by Patrick Lightbody and Jason Carreira.
| Memory Analysis in Eclipse |
| Enterprise AJAX - Transcend the Hype |
TEXTBOX:
TEXTBOX_HEAD: Using the if-then-else framework: Read the whole series!
:END_TEXTBOX
For convenience, Listing 1 below displays the implementation of the if-then-else framework for the example used in Parts 1
and 2. I suggest you review the use of Conditions, Actions, Rules, the Updateable interface, and the Invoker subclass before diving into the rest of the discussion. (You may also wish to compare my approach to coding branching logic
with the Hashed Adapter Objects design pattern, discussed in Mark Grand's book on design patterns -- see Resources for more information.)
Listing 1. The URLProcessor_good class with user-defined inner classes
class URLProcessor_good extends URLProcessor implements logic.Updateable {
static final String URL_STR = "urlString";
URLProcessor_good(){
super();
}
void decideUrl() {
try {
Invoker inv = new ConcreteInvoker((Updateable)this);
inv.execute();
}
catch(NestingTooDeepException ntde) {}
catch(IllegalExpressionException iee) {}
catch(RuleNotFoundException rnfe) {}
catch(DataNotFoundException dnfe) {}
}
/** Updateable interface implementation */
public void doUpdate(Hashtable ht) {
if(ht == null) return;
Object ob = ht.get(Action.MAIN_KEY);
if(ob != null && ob instanceof String) {
setUrl((String)ob);
}
}
static class OtherCondition extends Condition {
public static final String KEY = "key";
public OtherCondition(Hashtable ht) {
super(ht);
}
public Boolean evaluate() throws DataNotFoundException {
Object ob = null;
if(getData() == null || (ob = getData().get(KEY)) == null) {
throw new DataNotFoundException();
}
if(!(ob instanceof DataBank.Region)) {
return Boolean.FALSE;
}
DataBank.Region region = (DataBank.Region)ob;
boolean result = !region.equals(DataBank.WEST_REGION) &&
!region.equals(DataBank.EAST_REGION);
return new Boolean(result);
}
}
static class MemberCondition extends Condition {
public static final String KEY = "id";
public static final String TABLE = "table";
public MemberCondition(Hashtable ht) {
super(ht);
}
public Boolean evaluate() throws DataNotFoundException {
Object id = null, table = null;
if(getData() == null ||
((id = getData().get(KEY)) == null) ||
((table = getData().get(TABLE))==null) ||
!(table instanceof Hashtable)) {
throw new DataNotFoundException();
}
Hashtable members = (Hashtable)table;
return new Boolean(members.containsKey(id));
}
}
public class ConcreteInvoker extends Invoker {
public ConcreteInvoker(Updateable ud) throws NestingTooDeepException {
super(ud);
}
public void loadRules() throws NestingTooDeepException {
rules = new Rules();
try {
// Conditions: Actions:
// East|West|Other|Lim|Member
rules.addRule("TFFT*", new Action(ud,EAST_PRIVILEGED));
rules.addRule("TFFF*", new Action(ud,EAST_NOT_PRIVILEGED));
rules.addRule("FTFTT", new Action(ud,WEST_MEMBER_PRIVILEGED));
rules.addRule("FTFTF", new Action(ud,WEST_NONMEMBER_PRIVILEGED));
rules.addRule("FTFFT", new Action(ud,WEST_MEMBER_NOT_PRIVILEGED));
rules.addRule("FTFFF", new Action(ud,WEST_NONMEMBER_NOT_PRIVILEGED));
rules.addRule("FFT**", new Action(ud,OTHER_REGION));
}
catch(NestingTooDeepException e) {
throw e;
}
}
public void loadConditions() throws IllegalExpressionException {
try {
conditions = new Vector(5);
conditions.addElement(new Condition(db.getRegion(),DataBank.EAST_REGION));
conditions.addElement(new Condition(db.getRegion(),DataBank.WEST_REGION));
Hashtable other = new Hashtable();
other.put(OtherCondition.KEY,db.getRegion());
conditions.addElement(new OtherCondition(other));
conditions.addElement(new Condition(db.LIMIT_THRESHOLD, Condition.LESS,db.getLimit() ));
Hashtable mem = new Hashtable();
mem.put(MemberCondition.TABLE, db.getWestMembers());
mem.put(MemberCondition.KEY, db.getUserId());
conditions.addElement(new MemberCondition(mem));
}
catch(IllegalExpressionException e) {
throw e;
}
}
}
}
The following issues, which I outlined at the end of Part 2, will provide a structure for this discussion:
loadRules() method spell out the intended order of evaluation. I always consider conditions in the following order, whether I am loading
conditions or creating a sequence of Booleans: East, West, Other, Limit, Member.But what happens when the number of conditions grows to 15 or 20? You can no longer expect to rely on a difficult-to-read set of notes in your documentation as a reliable means to guarantee adherence to a particular order of evaluation. Clearly, this part of the framework begs for an automated solution that can eliminate human error.
sampleCode2 and sampleProfile. The sampleCode2 package shows how to use sequencers in URLProcessor_good; the package name for this GUI code has changed to sampleCode2, and so the HTML file that runs the applet had to be changed to iteGUI2.html. The sampleProfile package contains the Profiler class and the Main class, with which you can test performance. The files were compiled with JDK 1.1.7, and the GUI was written in Swing. If
you attempt to recompile, be sure to include the swingall.jar file for Swing 1.0.3 in your classpath. Download the zip fileHashtables in Chapter 11Hashtable lookups. Some obvious differences between Grand's pattern and my framework are: