| |||||||||||||
Feel free to participate in discussion of Theseus.
To subscribe, send email to
theseus-user-subscribe@yahoogroups.com.
The difference between Theseus
and Struts: Well, first of all we did try to contribute
to Struts, but because of the rather large following Struts has, the changes we would
have liked to see made would have broken everybody's apps already depending on it.
Secondly, Struts has many features Theseus does not such as international support, tag-libs and so on.
The things that we don't think Struts does that
Theseus does is automatic XSL support by simply supplying a 3rd attribute to
the
Nested auto-population is the ability to have instance fields of objects auto populate. For example, Class A has an instance field of type class B. With auto-population of nested objects, you can give a form field on a page a name like B_FirstName and the instance of B would get populated (its setFirstName() method would be called). It works at any level deep, so if class B had an instance field of class C, the form field might be name="b_c_SomeProperty". Theseus would see the _, assemble the name getB()., see the next _ and the name would now be "getB().getC()." and finally no more _ so the name becomes "getB().getC().setSomeProperty()". Therefore, by using an _ (we will later change this to make the specific character configurable via servlet init params) in the name of a form field NAME attribute, you can get nested objects auto populated, as well as those in the javabean instance itself (just give the name of the method (or property) and your all set).
Auto-populating only when instructed to simply means that when you pass a request parameter that says "force auto-population of this form", the auto-population is invoked. Otherwise it is not. We requested this for Struts but as the Struts team pointed out (which we can see and agree to their point on this), the idea behind Struts auto-population is to maintain a server-side state of the client at all times. We felt auto-populating a bean with half-done fields if the user hits CANCEL (for example), was a waste of CPU time. When a user hits Cancel, they usually don't want to be on that page, and probably don't care about what they just entered. Of course, you have the option to force auto-population on every form by simply submitting the attribute (which the name is configurable in web.xml) with all requests.
Command pattern development, at least in the terms of servlets, usually means passing a request attribute (the name "command" is probably very common) that usually passes in a number that indicates the method to be called allowing more than one submit button on a page to submit the form to the same servlet but use a different method. For example, it is common to see something like:
int command = Integer.parseInt(request.getParameter("command"));
switch(command)
{
case 1: thisMethod();
case 2: thatMethod();
case ME: meMethod();
}
The case 1 or 2 simply indicates the number that was passed in, and a different method is called based on the "command" number passed in. ME is the same thing but shows an example of a constant that would normally be defined somewhere in the class or super class..making it harder to figure out the exact value for the method call.
What we have done to make Theseus a bit different is to have the command be the last part of the name of a method to call. Using reflection in the ControllerServlet, we create the name "executeCommandXXX()" where by XXX is the name of the "command=" passed in. If one is not passed in, the name "Default" is used. This allows you to more easily find the method in a specific action class, simply by looking at the page, seeing the last part of the name, and then opening up the source and jumping to that method. Of course, if the source descends another action class (for example, a more generic class that includes some methods that other more detailed action classes would all use), its possible the method could be in their parent class and that would then have to be looked into. None the less, we think it is much easier this way, than having to look at the number in the form submit field (button, graphic, whatever), then looking for a switch() statement in the source (or possibly a method like dispatch()), then looking for the method associated with the number. Even worse, if the switch() uses constants, you then have to look for the constants, and often constants are defined in a parent class, which means more work. This also should make it easier to come in and pick up on someone elses work. If given decent names, its easy enough to figure out what each method does in that class. For example, a button labeled CANCEL calls a method with command=Cancel is clear enough to figure out what its intentions are. If a button is labeled "Open an existing item" but the command=Search, its still clear enough to realize that "Open an existing item" means you will be doing a Search (probably in a database) for something based on some criteria (user name/id, etc).
So lets put these few things together for a simple example. We'll start off with a jsp page form, the javabean class (used on the page and in the action class) and then the action class.
<jsp:useBean id="MyBean" scope="session"
class="com.mycompany.beans.MyBean"/>
<form name="MyForm"
action="/ActionName.do?command=Submit&auto-populate=true"
method="post">
<input type="text" name="FirstName" value="<%= myBean.getFirstName() %>">
<select name="state" size="1">
<option value="CA">CA</option>
<option value="NV">NV</option></select>
<input type="submit" value="Submit this form">
<input type="submit" value="Cancel"
onClick="this.form.action='ActionClass.do?command=Cancel'">
</form>
---------------
java bean class
---------------
package com.mycompany.beans;
import com.mvc.beans.*;
public class MyBean
{
private String state = "";
private String firstName = "";
public MyBean()
{
}
public String getState()
{
return state;
}
public String getFirstName()
{
return firstName;
}
public void setState(String value)
{
state = value;
}
public void setFirstName(String value)
{
firstName = value;
}
}
-----------------
java action class
-----------------
package com.mycompany.actions;
import com.mvc.action.*;
import com.mvc.beans.*;
public class ActionClass
extends GenericAction
{
public String executeCommandDefault(ActionContext context)
{
return "DefaultOK";
}
public String executeCommandSubmit(ActionContext context)
{
MyBean bean = (MyBean) context.getFormBean();
System.out.println("First name is " + bean.getFirstName() );
System.out.println("State is " + bean.getState() );
return "SubmitOK";
}
public String executeCommandCancel(ActionContext context)
{
return "CancelOK";
}
}
Above you have a JSP page (left out the obvious HTML for a complete page), a javabean class used on the JSP page as well as in the action class, and an action class. First, the JSP page defines a form, with two fields. A first name text box, and a drop-down state selection box. When the form is submitted via the Submit button (the default action if you look in the <form..> tag which is why there isn't an onClick handler for that button), the request (along with the form data) is sent to the ControllerServlet (via the web.xml mapping which will be discussed how to do so in another article if you don't already know). The ControllerServlet sees the auto-populate=true parameter which tells it to go ahead and try to populate the javabean mapped to this action name, with any request parameters it finds that have matching attribute names as methods in the javabean class. Next, the ControllerServlet calls the action class method after constructing the executeCommandSubmit() method name. Since in the action class there is a method called executeCommandSubmit(), that method is executed. In that method, the first line says to get the javabean instance that is mapped to this action. Because the mapping would contain the SAME attribute name of that used in the id="MyBean" on the JSP page, and they are session scope, the instance reference refers to the same one java bean!! That is important to remember. So, by the time your action method is called, the javabean has already been auto-populated and any fields from the form are now already available for you to use without having to call request.getParameter("Param-name"). So, if you typed in "My Name Is" in the input box, and selected "NV" from the state box, the output would display:
First name is My Name Is
State is NV
If you hit the Cancel button, the executeCommandCancel() method is called, however, because auto-populate is not part of the request, the auto-population routine is ignored therefore your app is not wasting valuable cycles using the reflection code of the ControllerServlet iterating over every parameter and trying to match them up to a method in the bean. This is one difference where Struts always does this and Theseus does not.
The focus here is 1) the auto-population is done only on the Submit method, not the Cancel method because the auto-populate=true is not submitted as part of the request with the Cancel button. 2) The easy process in which creating action classes, javabeans, jsp pages and how easy it is for them to interact. This should prove to provide quick development. It is very simple to open a JSP page (or do a View Source on a web page), look at the submit button, look at the Action name and the command= name, open up the action class, and jump to that method. JavaBeans couldn't be more simplistic..just have getter/setter methods to handle them. 3) Struts has each action method pass a few parameters and return an object. Every action method has to have the same signature, unless a dispatch() method in the action class calls each method with different parameters. In Theseus, you simply get passed an ActionContext. It is an object that is created for each request that stores the HttpServletRequest, HttpServletResponse, HttpSession, Servlet, Mapping and some helper method (getCommand(), getUserAgent(), getUserIp(), etc). At any point in your action method, you simply call context.getRequest(), context.getSession(), etc. We have even added convenience methods: context.getParameter() same as request.getParameter(). Feel free to look at the MVC source in ActionContext class and you'll see all the methods. Theseus no longer supports the Servlet 2.1 specification. Almost all vendors of servlet engines have updated to the Servlet 2.2 specification, and compiling produces warnings about deprecated methods when a servlet 2.2 engine is used. Another thing Struts does is return a Forward object. Its simple enough to do a return Mapping.findForward("MyString"), but we like the simpler syntax of return "MyString" and allow the ControllerServlet to do the lookup for you. In other words, we have made Theseus as automated as possible so development is faster, easier, and more manageable. In doing so, the theseus.jar file (which is all you need to use Theseus), is a mere 16K (as of May 2001) in size. It will probably grow a bit more as some more stuff is added, but it is a pretty small addition given the functionality it will enable for you. Keep in mind it also has automated XSL processing using the Saxon (or Xalan) processor, and the latest Xerces xml parser. We will explain this in another document as well.
Feel free to participate in discussion of Theseus.
To subscribe, send email to
theseus-user-subscribe@yahoogroups.com.