Blogs : Archives

As a contribution to Simon Brown's series of articles where he compares Java web application frameworks, I created a RIFE version of the read-only blog that currently serves as an example.

What is RIFE?

RIFE is a full-stack component framework to quickly and consistently develop and maintain Java web applications.

I'd like to underline that we focus on maintainability and development comfort. We firmly believe that it's important to have a high-level, self-documenting overview of your web application. This allows teams to work independently and simultaneously on different parts. At any moment they can integrate their work without stepping on each other's toes. This approach led to the creation of a site-structure that handles both the logic flow and the data flow, as a state machine. Any modification in there will correctly propagate throughout the entire framework, reducing repetition and redundancy to a minimum. I will explain more about the site-structure later in this article.

Let's dive into the example. Basically, we have an application with three pages. The Home page displays a list of blog entries, which can optionally have an excerpt. When an excerpt is present, a link will be available towards to full entry Detail page. Also, in case a user tries to access an invalid blog entry, a nice Not Found page will be displayed.

Abstract base element

Each page in RIFE is implemented through an element. Each of them requires access to a blog service and will print out a template that contains the name and the description of the blog. I created the following abstract base class that will be extended in the concrete elements:

The initialize() method functions like a default constructor, except that it is called when the entire web context is set up, and getBlog() is simply a convenience method for the extending classes.

Reusable template blueprint

Apart from the page-specific content, the layout of each template will be exactly the same. I thus created a common blueprint that will later be included into the other templates.

blog/blueprint.html

<!DOCTYPEhtmlPUBLIC"-//W3C//DTD XHTML 1.0 Transitional//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<htmlxmlns="http://www.w3.org/1999/xhtml">

<head>

<metahttp-equiv="Content-Type"content="text/html; charset=UTF-8"/>

<title>${v name/}</title>

<basehref="${v WEBAPP:ROOTURL/}"/>

<linkrel="stylesheet"href="style/screen.css"type="text/css"/>

</head>

<body>

<divid="container">

<h1>${v name/}</h1>

<h2>${v description/}</h2>

${v page content}${/v}

</div>

</body>

</html>

The highlighted tags are values (v tag) that function as placeholders and have unique names. When the tag has both a beginning (${v name}) and an ending (${/v}), the enclosed content will be used as the default when nothing has been assigned to it. The 'WEBAPP:ROOTURL' value will be automatically replaced by RIFE with the root URL of your web application. This makes it easily relocatable under another name, included into another application or onto another web server.

Home element

This is the implementation of the home element. It simply iterates over the blog entries and displays them in the template. If the excerpt contains data, it will create a "more" link that carries over the entry ID towards another element. The template is printed at the end.

blog/Home.java

package blog;

import domain.Blog;

import domain.BlogEntry;

publicclassHomeextendsAbstractBlogBase{

publicvoid processElement(){

Blog blog = getBlog();

for(BlogEntry entry : blog.getBlogEntries()){

template.setBean(entry);

if(entry.getExcerpt()!=null){

setOutput("entryId", entry.getId());

setExitQuery(template,"more");

template.setBlock("entry content","excerpt");

}else{

template.setBlock("entry content","body");

}

template.appendBlock("entries","entry");

}

print(template);

}

}

Since the display of each entry will be the same for this element and the next Detail element, I first extracted that into a reusable template snippet. This snippet will later be included in other templates through the include tag (i tag).

blog/entry.html

<divclass="blogEntry">

<h3>${v title /}</h3>

${v entry content/}

<p>Posted on ${v date/}</p>

</div>

The template of the home element will now include the blueprint that we created earlier and this snippet. It also introduces a new concept, template blocks (b tag). These will be stripped out and are available to construct your layout with. You'll also see a special block, the block value (bv tag) which will automatically assign its content to the value with the same name. In this case, the "page content" value will be filled into the blueprint.

blog/home.html

${i blog.blueprint/}

${bv page content}

${v entries}${/v}

${b entry}

${i blog.entry/}

${b excerpt}

${v excerpt/}

<p><ahref="${v EXIT:QUERY:more/}">Read more</a></p>

${/b}

${b body}${v body/}${/b}

${/b}

${/bv}

Note that there's a special value tag with the prefix "EXIT:QUERY:". The content of these tags is generated by RIFE to target the correct elements and at the same time carry over the relevant view state.

Detail element

The detail element receives the ID of the entry that it has to display, it will be injected through the corresponding setter method. When it can't find an existing entry, it simply exits to display the "not found" page:

blog/Detail.java

package blog;

import domain.BlogEntry;

publicclassDetailextendsAbstractBlogBase{

privateString id;

publicvoid setId(String id){this.id = id;}

publicvoid processElement(){

BlogEntry entry = service.getBlog().getBlogEntry(id);

if(null== entry) exit("not found");

template.setBean(entry);

print(template);

}

}

The template of this element contains nothing new and reuses the blueprint and the entry templates again. However, instead of conditionally setting blocks to fill in the "entry content" value in the blueprint, it does so directly with a block value tag.

blog/detail.html

${i blog.blueprint/}

${bv page content}

${i blog.entry/}

${bv entry content}${v body/}${/bv}

${/bv}

NotFound element

The NotFound element indicates that an entry wasn't found by setting the correct HTTP status code and printing out the template:

blog/NotFound.java

publicclassNotFoundextendsAbstractBlogBase{

publicvoid processElement(){

setStatus(404);

print(template);

}

}

The template is trivial:

blog/notfound.html

${i blog.blueprint/}

${bv page content}

<h3>Page not found</h3>

${/bv}

Meta data through constraints

Simon's domain model was left untouched. However, the application requirements specify richer information about the data model: the date has to be formatted according to the blog's timezone and locale, and the excerpt and body properties contain HTML instead of text. Since RIFE automatically encodes text that is filled into the templates, you need to indicate that it has to display these text fragments raw. Instead of handling this everytime individually throughout your application, it's better to centralize this as meta data that augments the BlogEntry bean. RIFE will detect this meta data when it interfaces with BlogEntry instances, causing RIFE's functionalities to adapt accordingly.

This is the meta data class that will be automatically picked up by RIFE and merged into the BlogEntry class:

domain/BlogEntryMetaData.java

package domain;

import com.uwyn.rife.rep.Rep;

import com.uwyn.rife.site.ConstrainedProperty;

import com.uwyn.rife.site.MetaData;

import java.text.DateFormat;

publicclassBlogEntryMetaDataextendsMetaData{

publicvoid activateMetaData(){

BlogService service =(BlogService)Rep.getProperties().getValue("blogService");

Blog blog = service.getBlog();

DateFormat format =DateFormat.getDateTimeInstance(

DateFormat.LONG,DateFormat.LONG, blog.getLocale());

format.setTimeZone(blog.getTimeZone());

addConstraint(newConstrainedProperty("date").format(format));

addConstraint(newConstrainedProperty("excerpt").displayedRaw(true));

addConstraint(newConstrainedProperty("body").displayedRaw(true));

}

}

Site structure

RIFE's site-structure is the basis of most of the neat features in the web engine, most of which are not applicable to Simon's simple blog example. The declaration of the site-structure can be done in a variety of ways. At the core we have pure Java builders with fluent interfaces (chainable setters) which can be accessed directly, but we additionally have a collection of wrapper declaration languages like Groovy, Janino and XML. The declaration can also be fully automated through the registration of custom handlers, which is for example used by RIFE/Crud to automatically generate entire CRUD interfaces from your domain model. You can freely choose which declaration method you prefer and mix them together. Everybody is thus able to use what feels the most appropriate and the most comfortable.

In this example, I opted for the XML declaration method, since it's the most widely used by our users. The reason for this is that it allows you to easily intervene in existing application flows without having to recompile or require access to the sources. You can also easily combine several projects and link their logic flow and data flow together, or change the site-structure on a production server without having to redeploy the entire application. However, please remember, if you have any aversion to XML, you're free to use the plain Java method instead, if that's what you prefer.

You can clearly see that each element has an ID and an implementation. They also declare a "template" property that will make RIFE inject the appropriate template instance when the elements are processed.

The Home element is the arrival of the site, which can be seen as the default element (something like index.html in static sites). It also has one flow link towards the Detail element that contains a data link to carry over the entry ID.

The Detail element will handle pathinfo data. Through a mapping declaration, the entry ID will be extracted from URLs like this: "/detail/3". Additionally, every link that RIFE generates towards the Detail element will use this pathinfo mapping to avoid the presence of query parameters.

The NotFound element hasn't got an URL, since it's only accessed when the "not found" exit is triggered.

Servlet containers and the repository

RIFE takes care of the entire life-cycle of your application through what we call the Repository. By adding the RIFE filter to web.xml, your application co-exists freely in the same URL space as any other web application. This is the only change that you will ever have to do there, and by using the RIFE base archive or the Jumpstart as a starting point, you'll never even have to touch this file. The rep.path init parameter points towards the XML declaration of the repository which is looked up through the classpath. Alternatively, you can use the lifecycle.classname init parameter and point it towards your custom life-cycle class that sets up its own repository in pure Java without XML.

This is the relevant part of the web.xml file:

<filter>

<filter-name>RIFE</filter-name>

<filter-class>com.uwyn.rife.servlet.RifeFilter</filter-class>

<init-param>

<param-name>rep.path</param-name>

<param-value>participants.xml</param-value>

</init-param>

</filter>

<filter-mapping>

<filter-name>RIFE</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

</web-app>

The repository contains a number of participants and application-wide properties. Participants will be started up and shut down together with your application, and they serve as object factories or services. RIFE will handle inter-participant dependencies transparently and figures out the appropriate execution order.

BlogServiceProvider is a custom participant that gives access to a BlogService instance that will be used as an injected property throughout the entire application. This participant interacts directly with the domain model that Simon wrote and looks like this:

blog/participants/BlogServiceProvider.java

package blog.participants;

import com.uwyn.rife.rep.SingleObjectParticipant;

import domain.BlogService;

publicclassBlogServiceProviderextendsSingleObjectParticipant{

publicObject getObject(){

returnnewBlogService();

}

}

The second participant initializes the site structure by using the blog.xml declaration that I explained before.

Comments

Re: Comparing Web Frameworks: RIFE

This I figured out what framework authors are doing wrong. URL talks about the issue that a lot of new Java frameworks are not simple to learn as they include a lot of new concepts/jargon and buzwords which is a bit too much for the normal developer.The list of framework include JSF and yes RIFE.

To be very honest ... for a normal struts developer learning RIFE does seem a bit daunting.

Any initiatives on making it simpler and more understandable?

posted by Tabish A.Shaikh on Mar 16, 2006 8:23 AM

Re: Comparing Web Frameworks: RIFE

Hi Tabish,

thanks a lot for your comment. I tried very hard to make this article as easy to understand as possible. I'm very interested in knowing what in particular seems daunting to you. We really consider lowering the entry barrier as much as possible a priority, and feedback about this is thus very appreciated.

Best regards,

Geert

posted by Geert Bevin on Mar 16, 2006 8:28 AM

Re: Comparing Web Frameworks: RIFE

Hi Geert,

there are too much infos and features to understand.
i could follow your text up to the chapter "not found".
Then you left me.
I think metadata constraints are great,
but not needed in the scope of an intro.
it's nice to have a lot of options for the template and the dataflow,
but at the start it is more confusing.
show only the rife way of doing: 3 Elements and one xml.
Keep it simple. rife is very attractive with its simple parts.

your conclusion doesn't say anything about the title.

posted by m. schmitt on Mar 16, 2006 10:32 PM

Re: Comparing Web Frameworks: RIFE

Hi,
thanks for your comments. Actually while this is an introduction article I was really tied to the features that Simon Brown decided upon. Of course I wanted to implement them in the best possible way using RIFE. I agree that it might require a bit more effort than a casual read, in time I'll get the hang of explaining everything in a simple way

In the meantime though, I don't want to explain RIFE without anything specific to the framework. It would look very similar to most other things out there. If that's all that people see, there wouldn't be much point in looking further at RIFE.

I agree that I should maybe have talked about the options in a footnote. I mentioned them since I constantly get a lot of wind from all sides, which is always easily defeated by showing the alternatives.

You're right about the conclusion. It's not my place to compare RIFE with the other frameworks, it would simply be too biased. I followed the same approach as SImon's articles and the Wicket one, by just summarizing the strong points of the framework. It's up to the readers to do the actual comparison.

I'm still interested though in what exactly was unclear to you and why you couldn't follow anymore at a certain moment. Maybe I explain things wrongly.

Best regards,

Geert

posted by Geert Bevin on Mar 17, 2006 12:29 AM

Re: Comparing Web Frameworks: RIFE

Geert,

I have gone through the Live Guide a few months ago and when I was learning RIFE what could have helped me a lot would be an introduction to the concepts and components that make RIFE before jumping into an example.

An architectural overview of what RIFE is and what it is made up of would be very helpful. I see a lot of interesting things in RIFE and a detailed guide/online book would be very helpful to market RIFE.

Kind Regards,
Tabish

posted by Tabish A.Shaikh on Mar 17, 2006 6:25 AM

Re: Comparing Web Frameworks: RIFE

Hi Tabish,

I totally agree, actually I have parts of this done in my TSSJS presentation. After the conference I'll put in on the site and add some more details about the architecture of the web engine itself.

Best regards,

Geert

posted by Geert Bevin on Mar 17, 2006 7:10 AM

Re: Comparing Web Frameworks: RIFE

excellent

posted by Anonymous on Mar 19, 2006 6:46 AM

Custom Queries with Rife/Crud

Hi Geert,
thanks for this great article. What I am wondering is how easy it is to create custom queries with Rife/Crud. I've looked around for some documentation and/or examples but I couldn't find any.

And also do you have any performance metrics comparing Crud with Hibernate, iBatis, etc?

posted by Cagan Senturk on Mar 23, 2006 11:12 AM

Re: Comparing Web Frameworks: RIFE

Hi Geert. One quick question / observation: Is there a way to make outputs in a flow link map to inputs of the same name, i.e. all outputs become a datalink with matching in/outs. If not, this should be the default. In general, it seems to me that Java frameworks could benefit from providing a lot more "defualt" behavior, and less required configuration.

Also, since you are so responsive (you are all over the Web, man!), I will give you the opportunity to plug RIFE for my situation. The Ruby/RAILS philosophy, what they really get, is that productivity is king now. Most of the Enterprise requirements, the ones that the J2EE went overboard on, can be addressed later in development.

So, after seeing RAILS in action (though not actually using it), I have to think that there is a way to pull together java technologies and frameworks to approximate that productivity. I'm especially intrigued by the scaffolding generation stuff. For my next project, I am looking at:

- Spring + Hibernate, using AppFuse or Equinox, or maybe AndroMDA, to generate as much as possible.
- RIFE (w/ Jumpstart, CRUD)
- Some other framework???

Any quick thoughts on how RIFE can be more productive than the alternatives, even for a newbie? Thanks much.

Hi Cagan, I'm at TSSJS in Las Vegas at the moment and don't have access to the internet during the day. I'll put up an example of creating custom queries with RIFE/Crud in a few days, it's very easy. I also need to put up some javadocs, kinda forgot about that.
Basically, you inject an element that extends a standard RIFE/Crud element and replaces it for a particular domain model. In the custom element you can then override the prepareQueries method and customize what the standard version does (http://rifers.org:8088/.../Browse.java?...).

Hope this already gets you started.

Geert

PS.: it would be better to ask questions on the mailinglist since other people can answer more easily too then

posted by Geert Bevin on Mar 24, 2006 9:47 AM

Re: Comparing Web Frameworks: RIFE

Hi John,

currently this is not possible, but for the next release we'll add an autolink feature that does just this. In the meantime you can create groups and gobalvars inside those groups. This will create the inputs/outputs/datalinks for those globalvars.

Wrt to RIFE being more productive than other solutions. First, being a full stack gives a very functional solution without the configuration of all the libraries, nor any depency problems. The layers are also able to provide you with much more high-level functionalities since they can easily talk together, being that they were part of the same design.

RIFE/Crud also gives you model driven functionality generation at runtime without code generation, that makes it easy to customize and extend, and still to continue to rely on the generated features without having to worry about code being replace during regeneration, etc etc.

Also, the RIFE component model allows you reuse anything you write in any context (portlet-like mini app, AOP-like overlaid site, page collections, pages, widgets, ...). This is a huge benefit once you start to leveraging it in your applications.

There are many more, please feel free to contact the list to discuss this further.

Going to bed now.

Geert

posted by Geert Bevin on Mar 24, 2006 9:57 AM

Re: Comparing Web Frameworks: RIFE

I'm new to web frameworks, but creating 14 seperate files for a such a simple application seems way, way overboard. That right there would lead me to believe it's not quite as easy as advertised.