Simplets for Simple Servlet Web Applications in Java

A very simple framework for running web applications as servlets.  

The package is unusual because it has absolutely no HTML template language.  All HTML is generated by simple and direct Java code.   This completely avoids the need to cross the impedance mismatch between the template language and the Java code.  

This makes it easy for applications to use reusable chunks of HTML that are only defined once.   It avoids the need to manually paint common components into pages.  It also makes it much easier to drive their layout from meta data, and thus be much more flexible.   In practice, experienced programmers do not use screen painters anyway, prefering to write in raw html.  Much better to write reusable Java.

(This project was renamed from SimpleServlets as someone else had a very crude project called SimpleServlets.)

This is from the www.simpleorm.org project.  It is distinct, but has the same philosphy of simple, direct, non-reflective object oriented design.  (Avoiding reflection means you get static compiler checking, you can easily find references to objects in an IDE, you can structure the code how you like, refactoring is safe and easy and there are no suprises.  No magic strings or method names etc.)

The code can be found at
  svn co http://simpleorm.svn.sourceforge.net/svnroot/simpleorm/trunk/simplets

For easy development it is set up to be invoked as an ordinary Java application that listens on Port 8080 (using Jetty).  No need to "deploy" to a web server unless you really want to, although this can easily be done as it is standard Servlet techology.

The test cases can be Invoked by just running test.HTestJettyMain and browse to localhost:8080.    (This is the default Ant task.)

There are two distinct modes of operation, namely a very simple procedure based approach or a more sophisticated but complex component model.

Procedural Requestlets Approach

In the procedure mode, the user simply overrides a method that outputs HTML directly, much like raw Servlets.  

However, their is a tiny controller servlet that creates a new "requestlet" instance for each invocation. This instance can then conveniently store the request and response objects etc.  It also simplifies web.xml configuration.  

(Creating one additional instance per request adds zero overhead in practice.  It reduces the temptation to introduce threading errors by creating Servlet instance variables.  It should have been done in the original Servlet standard.)

The requestlet provides methods like outHtml("<IMG .../>"), pushHtml("<td>"), popHtml("</td>") and outEscaped(userInput) to output Html.  (popHtml checks for matching pushHtml, and outEscaped escapes Html characters such as '<' and '&').

However these are not normally called directly, but are wrapped in simple methods.  For example,  public void _td() {popHtml("</td>");} just outputs "</td>" (and check that it matches a <td>).  hi.fieldPrompt outputs a prompt and an <Input...>.  The methods are very simple, so it is very easy to create your own methods to as required.

This means that if you use a consistent style that there is very little need to use raw HTML in your forms.  This in turn means that a template language is unnecessary.  And all the complexity of the resulting impedence mismatch can be avoided.

State is stored via "paramattrs", which is a simple wrapper around request.getParameter and  request.get|setAttribute (attributes override parameters).  

So an example of a simple form is
public class TestPlainRequestlet extends HPlainRequestlet { ...
  @Override protected void onBody() throws Exception {
    outHtml("Some Raw HTML<br/>");

    hb.strongPrompt("Hello TestRequestlet"); hb.br();
    // Outputs: <Strong>Hello...</strong><br>
// hb is just a convenient static final in HPlainRequestlet

    hb.localImg("frameworks-BallAndChain.jpg"); hb.p(); // Resolves context
    hb.a(TestServlet.componentTestFactory); hb.p(); // Auto generates link from shortTitle.
    
    hi.form();
    hb.prompt("Product Name: "); hb.data("Pickles & Pianos"); hb.p();
       
    initParamattr("My Field", "Hello <My> Field"); // iff not already set.
    hb.addFieldError("My Field", "Bad MyField");
    hi.field("My Field", 20);
   
    initParamattr("f.box1", "yes");
    hi.fieldPrompt("f.box1","f.box1"); hi.checkbox("f.box1"); hb.br(); // <label for= ...
    hi.fieldPrompt("f.box2","f.box2"); hi.checkbox("f.box2"); hb.br();

    hi._form();
    hb.outputAllErrorMessages();

  }
As these are just methods, with no objects being created, the forms run very fast.  There is nothing in this package that would take more than a few hours to learn by simply looking at the tiny source code.

Component Requeslet

There is an alternative, more sophisticated component system oriented.  There is a simple event model that creates an object graph of components, loads field values into them, performs business logic, and finally renders the HTML.  Thus each component can have multiple entry points and can wrap sub components effectively.  

Pages normally post back to themselves, and automatically resent to the user if field validation fails.  Business logic can be added to components, or placed in the main Requestlet methods.  There is also a method to provide custom renderers for deep customization.

An example of a simple form is below

 public class TestComponentRequestlet extends HComponentRequestlet { ...

HPage page = setPage(new HPage());

{ page.addNewHtml("Some Raw HTML<br/>"); } // rendered later.
{ page.add(MyStandardHeader()); }

{ page.add(new HErrorMessages()); } // Display any error messages

HFieldGroup group = page.add(new HFieldGroup()); // <table>...</table>
HField f1 = group.add(new HField("One")); // <tr><td>One</td><td><Input...></td></tr>
HField f2 = group.add(new HField("Two").setPrompt("The Second Field").beInteger());

ArrayList<Map<String, String>> myList = new ArrayList();

////// Table performs elem.get("Second") on each element in myList.
HTable table2 = page.add(new HTable(myList));
{ table2.add(new HMapColumn("First")); }
{ table2.add(new HMapColumn("Second")); }

HSubmittingComponent sub = page.add(new HSubmit("DoGoodThings"));

HSubmittingComponent sub2 = page.add(new HSubmit("DoBadThings") {
@Override public void onSubmitted() {
f1.setValue("bad");
}});

///// Main Class ///////

@Override public void onSubmitted() {
int myint = (Integer)f2.getValue();
...
}
The style of using initialization comes from SimpleOrm, and is handy but not compulsory -- it is all plain non-reflective code. SimpleOrm queries return objects that implement an array of maps, so can be used directly with things like Table.

The component model is elegant, but it is also work in progress, and is not complete.  If you do not want to help flesh it out then use the Plain Requestlets.  That said, the component model is more elegant and still very simple.

Alternative Technologies

* JSP based (eg Struts).  Can be made to work, but really ugly and unnecessary.

* ASP.Net.  OK if you sign up to Microsoft.

* JSF.  A badly designed mess.   Confused event model based on JSPs (Facelets does not fix this).  Bad lifecycles.  Template langauge with reflection based access only.   Horrible to write new components for.  Difficult to debug.  A copy of ASP.NET but done all wrong.  

* Seam.  Wallpaper of JSF.  Just say no.

* Tapestry.  Loved by some.  Wierd system of attaching code to plain Html which as magic attributes added.  Do you really want to paint forms by hand?  Focus on efficiency means templates separate from state.  Many domain specific languages.  Single genius developer (H. Ship). Good book.

* Wicket.  Another template based system, but templates simple, more Java focused than Tapestry.  People have had success with it.  Weak documentation, and web site just tells you how good it is.  You need to write a lot more code to get anything done than Simplets components, plus a template.

* Lift.  Scala based.

* AJAX/GWT/Echo2/ZK/Flash etc.  To build highly intereactive forms.  But do you need/want to pay for this complexity?


Anthony@berglas.org


The superior pilot uses his superior judgment to avoid the need for his superior skills. -- Aviation adage that also applies to software.