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.