Hi folks,

I'm trying out something quite cool, I though I'd share with you.

Pretty long email, but I think this could be a killer feature for our
favourite fwk.

For some of you who don't know Play2 <https://www.playframework.com>, they
have (amongst very good other stuff) a neat feature called Scala Templates
<https://www.playframework.com/documentation/2.0/ScalaTemplates>.

The idea is to have statically typed templates, just like everything else.
The template defines a signature (named arguments), and it ends up as a
actual function. You write the template in Scala, using a mix of html and
code, and you must define the template's signature. You then call the
function to render the template, passing any required argument. Simple, and
safe.

This is not so common in MVCs. I don't know any pure Java fwk doing this.

It has great advantages, I won't explain the benefits of Type Safety here,
but it's definitely a killer feature. Type safety applied to web page
templates !

To be honest, this almost got me. For a moment I thought, damn, we're
f*cking laaate with Stripes. I was about to switch to Play for good.

But before this, and using a few days off I had, I wanted to figure out how
this would work in pure Java. Play is written in Scala which is a
completely different language, allowing for those neat templates. But how
can we do this in good old Java ?

So I started by creating a "typed text templating framework" that allows to
write templates using a subset of the JSP syntax, and to compile them to
strong-typed Java code.

The name is TTT, which stands for Typed Text Templates.

Here's how a simple template looks like (MyTemplate.ttt) :

<%!
  String foo;
%>
<div class="my">
  <%= foo %>
</div>

The JSP declaration ( "<%!" ) is used to define the signature of the
template. This one takes a single argument "foo" of type String. Then, it
has some text (html actually) and an expression. As you imagine, the
expression is replaced by its value when the template gets rendered.

The TTT compiler can be invoked in order to generate a fully-functional
Java class, that you can use in your code to render the template :

// somewhere in my code
Writer out = ...;
new MyTemplate("hey there !").render(out);

As you see, the signature of the template maps to constructor arguments,
and you have a render(out) method to spit out the template to a Writer. You
can also compose templates, but I won't explain this here for the sake of
brevity (really ?).

So far so good. I can :
1/ write my templates almost like a JSP
2/ compile the templates to Java code
3/ invoke the generated code and render the templates to a Writer

Now, how does this integrate with Stripes ? Can we remove JSPs and use TTT
templates instead ? Well, yes, we can !

Rendering a template from an Action looks like :

public Resolution doSomething() {
  // do stuff
  ...
  return new TemplateResolution( new MyTemplate("hello there") );
}

Very easy : the class TemplateResolution (implements Resolution) does the
job. All you need to do is pass a template to its sole constructor.

Let's try a to apply our good old "pre-action" pattern to this, with a
simple action+template :

public class MyAction implements ActionBean {

  public String getStuff() {
    ...
  }

  @DefaultHandler
  public Resolution doIt() {
    return new TemplateResolution( new MyTemplate(this) );
  }
}

And now the template (MyTemplate.ttt) :

<%!
  // template needs the action
  MyAction myAction;
%>
<html>
...
<div>
  <%= myAction.getStuff() %>
</div>
...
</html>

Again, very easy :
1/ The action bean returns a TemplateResolution, passing itself to
MyTemplate's constructor
2/ The template uses the bean, declated as an argument, as any other Java
object.

The big (huge) difference here with the current approach using JSP is that
everything is statically typed.

Let's compare this to the "regular" JSP approach :

public class MyAction implements ActionBean {

  public String getStuff() {
    ...
  }

  @DefaultHandler
  public Resolution doIt() {
    return new ForwardResolution("/WEB-INF/jsp/my.jsp");
  }
}

And the JSP :

<%@ page ... %>
<html>
...
<div>
  ${actionBean.stuff}
</div>
...
</html>

Several problems here :
* in the controller, the forward is... a plain string. The JSP could even
not be there at runtime, who knows...
* no args are passed to the JSP when forwarding. The context is usually the
request scope, which acts as a loose-typed hashmap...
* in the JSP, what is "actionBean" ? you need to assume somebody has tossed
an object that has a "stuff" property into some scope (request ? session ?
app ? ...). Again, no compile-time help. Ok you could use a scriplet and
cast, but still : do you have a guarantee it's there, and it's what you
want ?

The TTT approach removes all those issues, and "fills the blank" between
the Controller and the View layers. Using this technique, it's all
statically typed. No more expectations about the presence of an attribute
or anything.

It also brings lots of other benefits :
* more reusable views
* container-independent : same technology for serving web pages or spitting
out emails or anything
* support for composite templates (nested) - think "typed Stripes layouts"
* ...

Of course, the Stripes taglib doesn't work in .ttt templates, so it has to
be reimplemented as pure Java APIs, useable directly from the templates.
It's a bit long, there's a lot of them, and the mapping ain't necessarily
1:1. But it's a good opportunity to rethink the tags, and craft a nice,
fluent API.

An example of s:form. The classic one, JSP+tags :

<%@ taglib prefix="s" ... %>
...
<s:form beanclass="<%=MyAction.class%>">
  Fill me
  <s:text name="myProp"/>
  and
  <s:submit name="doIt" value="Click Me"/>
</s:form>

And the TTT equivalent :

<%! ... %>
<%
  StripesTags stripes = new StripesTags(out);
  try ( Form f = stripes.form(MyAction.class).build() ) {
%>
    Fill me
    <%= f.text( "myProp" ) %>
    and
    <%= f.submit( "doIt", "Click Me" ) %>
<% } %>

As you see, it's using standard Java code in order to call the Stripes
"tags". I'm trying various approaches, like the try-resource for tags that
have a body. Again, it's a good opportunity to review the existing tags,
and design clean APIs for those.

It is a pretty ambitious project : there are many domains involved
(compilation, IDE, etc). But it is challenging, and I think it really
provides added value.

Stripes is getting old, and without such kind of new features, it'll
probably die slowly in favor of Spring or any other MVC.

I looked at Spring to see if I could integrate in there. Could be done too,
and would work just fine. But I think SpringMVC is a beast, it's too big,
and I like the programming model and simplicity of Stripes.

The project is open and free for anyone to try out / contribute :
https://github.com/pojosontheweb/ttt

For the moment I have :
* template compiler : creates Java source code from .ttt files
* maven mojo : compiles templates when building the app (generate-sources
phase)
* IntelliJ IDEA plugin : compile on save

It's still mostly a POC and needs some work to make it production-grade,
but it's functional enough to play with.

What do you think folks ?

Cheers

Rémi
------------------------------------------------------------------------------
_______________________________________________
Stripes-development mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/stripes-development

Reply via email to