h1. The Web Framework Evaluation - Testing Stripes Framework

In this article series, we are going to explore web frameworks from a Java point of view. It covers Java based frameworks and frameworks based on scripting languages that can run inside of a Java application server. The latter are for example Ruby, Python, PHP, Groovy based frameworks. This article is taken from my eBook 'The Web Framework Evaluation'.

You can get the eBook at http://www.laliluna.de/shop.

Difference between the free article and the eBook in PDF format.

h1. Introduction

Stripes is a Java based web framework. It is pretty light and has a very short list of dependencies. It is an action request oriented framework, making use of annotations and conventions for configuration.

We have tested version 1.5.1 which is released under the Apache License Version 2.0. The framework develops slow or is pretty stable, depending on how you want to describe it. There is a final release about every 6 to 9 months. The test took place in March 2009.

Website http://www.stripesframework.org/

h1. Hello world

The hello world application intends to show how complex it is to setup a simple application. It renders an index page with a link. Following a link calls an action of the framework and stores a localized message. The user is send to a view displaying this message.

You can download the sample project helloworld at

http://www.laliluna.de/download/framework-evaluation-samples.zip

h3. Required steps h4. Create a new web application.

Copy the following libraries of the Stripes download to the WEB-INF/lib folder.

commons-logging.jar

stripes.jar

cos.jar (optional, can be used for file upload)

Modify the web.xml

Stripes is integrated using a servlet filter. The most important setting is the ActionResolver.Packages. It defines where Stripes is going to look for so called ActionBean classes. These classes are called, if you send a request to the web application.

  helloworld
  
		Stripes Filter
		StripesFilter
		net.sourceforge.stripes.controller.StripesFilter
		
			ActionResolver.Packages
			de.laliluna.examples.www
		
 	

	
		StripesFilter
		*.jsp
		REQUEST
	

	
		StripesFilter
		StripesDispatcher
		REQUEST
	

	
		StripesDispatcher
		net.sourceforge.stripes.controller.DispatcherServlet
		1
	

	
		StripesDispatcher
		*.action
	

  
    index.jsp
  

Create a resource file

The resource bundle for messages is stored by default in the file StripesResources.properties in the class root folder. In order to have support for common error messages, copy the StripesResources.properties from the Stripes download.

Add the message for hello world.

hello.world=Hello world
h4. Create an action class

All action classes need to implement the ActionBean interface to allow Stripes to inject the context.

Stripes uses a convention to map urls to ActionBean classes. If you use a package name like

de.foo.bar.HelloWorld, the URL to execute the class is /de/foo/bar/HelloWorld.action. If you add one of www, web, stripes or action to the path, the path starts from there.

Use de.foo.bar.www.HelloWorld to get a mapped URL like /HelloWorld.action.

@DefaultHandler defines which method is called by default.

package de.laliluna.examples.www;

import net.sourceforge.stripes.action.*;

public class HelloWorld implements ActionBean{
	private ActionBeanContext context;

	@DefaultHandler
	public Resolution sayHello(){
		context.getMessages().add(new LocalizableMessage("hello.world"));
		return new ForwardResolution("/hello.jsp");
	}
	public ActionBeanContext getContext() {
		return context;
	}

	public void setContext(ActionBeanContext context) {
		this.context = context;
	}
}

The index.jsp looks like

<%@ taglib prefix="stripes" uri="http://stripes.sourceforge.net/stripes.tld"%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

  Simple jsp page
  
	Go to hello world

	

The hello.jsp looks like

<%@ taglib prefix="stripes" uri="http://stripes.sourceforge.net/stripes.tld"%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

  Simple jsp page
  
	

Message

That's it.

h2. Architecture h3. Framework concepts

Core element of Stripes are the ActionBean interface and event methods. A class implementing this interface is executed if a corresponding request (URL) is send.

http://domain/myapp/HelloWorld.action

calls the default event method of the ActionBean

public class HelloWorld implements ActionBean{ 
	private ActionBeanContext context; 

	@Validate(required = true, minlength = 2)
	private String inputField;

	public String getInputField() {
		return inputField;
	}
	public void setInputField(String inputField) {
		this.inputField = inputField;
	}

	@DefaultHandler 
	public Resolution sayHello(){ 
		// do something
		return new ForwardResolution("/hello.jsp"); 
	} 

If the request specifies an event, then Stripes will call a public method with the same name.

http://domain/myapp/HelloWorld.action?save

calls the save method in the corresponding ActionBean. You can overwrite the default event name for a method or specify that a method is executed for multiple events.

The ActionBean is at the same time the model. Stripes writes input values into this class using public setters and executes validations configured as annotations.

The request processing is handled by a servlet filter and a servlet. The filter provides access to the Stripes configuration and picks the language of the user. The request is wrapped in a Stripes request and the language is stored in that request as java.util.Locale.

The servlet makes use of the chain of responsibilities pattern to handle a request. The default chain consists of the following steps


Every step can stop the continuation by returning a so called Resolution. A Resolution can be a page for rendering or an URL for redirecting. If the DispatcherServlet receives a resolution from a chain it will execute it.

h3. Extension points

While it is not possible to define a custom chain, there are a number of extension points. Firstly the class implementing the ActionBeanContext can be replaced. It is a useful place to store additional information like the current user instead of getting this information from the HTTPSession or a ThreadLocal.

Then the configuration can be customized using the web.xml. A configuration class is executed during startup. It loads a number of factories to create the ActionBeanContext, input converter, output formatters, do exception handling and many others. You can configure an alternative factory using the web.xml

In addition interceptors can be defined. Interceptors allow to intercept every element in the chain. Basically, you provide a class, implement and interface, add annotations and configure it in the web.xml.

@Intercepts(LifecycleStage.HandlerResolution)
public class SecurityInterceptor implements Interceptor{
	public Resolution intercept(ExecutionContext executionContext) throws Exception {
	// do something before
	Resolution resolution = executionContext.proceed();
	// do something after
	return resolution;
}

Global interceptors are a good place to integrate security or your preferred AOP framework.

Finally, you can add interceptors per class. A use case is to provide information in the request, you always need during rendering.

// storing countries we always need in the JSP	
@After(stages = LifecycleStage.ActionBeanResolution)
	public void prepareRequest() {
		List countries = Arrays.asList(new Country("de", "Germany"), 
			new Country("uk", "United Kingdom"));
		context.getRequest().setAttribute("countries", countries);
	}

How to integrate a business layer?

The classic option is to call your business logic from the event method of the ActionBean. In that case you might call a factory to create the business logic or just instantiate the business logic class.

	@DefaultHandler
	public Resolution sayHello(){
		HelloService service = BusinessFactory.createHelloService();
		String message = service.createHelloMessage();
		context.getMessages().add(new Message(message));

		return new ForwardResolution("/hello.jsp");
	}

Alternatively you could replace the BusinessFactory with a call to your preferred AOP container (Guice, Pico, Spring, ...) to return the service.

A more beautiful approach is to use an interceptor and inject (Inversion of Control) the business logic class into your ActionBean.

There are already plugins following this approach and allowing to integrate EJB3 and Spring. To illustrate the approach, I would like to show you my implementation using Picocontainer.

Picocontainer is an Inversion of Control Container. You can add classes to the container to later pick up properly wired instances.

		MutablePicoContainer container = new DefaultPicoContainer();
		container.addComponent(PicoIntegration.class);
		container.addComponent(new SampleServiceImpl()); // add as singleton

Picocontainer supports a lot of different approaches to wire dependencies, one is constructor injection. It uses the parameter of the constructor to inject dependencies.

The constructor of class PicoIntegration needs an implementation of SampleService, therefor Pico will look for an implementation of the SampleService interface in the container and pass it to the constructor.

public class PicoIntegration implements ActionBean {
	private SampleService sampleService;

	public PicoIntegration(SampleService sampleService) {
		this.sampleService = sampleService;
	}

When getting a class from the container, you will get a properly wired instance. Picocontainer creates a new instance of PicoIntegration and injects the SampleServiceImp before returning it to you.

PicoIntegration sample = container.getComponent(PicoIntegration.class);

The final step is to integrate Picocontainer with Stripes. We will use a Stripes extension package. It is configured as parameter to the Stripes filter in the web.xml

	
		Stripes Filter
.......
		
			Extension.Packages
			de.laliluna.examples.pico.extensions			
		

The package has only one class which is an ActionBean resolver used by Stripes to create ActionBean classes. It extends the existing resolver and overwrites to methods.

public class PicoActionBeanResolver extends NameBasedActionResolver {

	private static final MutablePicoContainer container = 
		new DefaultPicoContainer();

	// Add all business services here
	static {
		container.addComponent(new SampleServiceImpl());
	}

// called by Stripes when all ActionBeans are loaded
	protected void addActionBean(Class<? extends ActionBean> clazz) {
		super.addActionBean(clazz);
		container.addComponent(clazz);
	}
// called every time Stripes needs an ActionBean
	protected ActionBean makeNewActionBean(Class<? extends ActionBean> type, 
		ActionBeanContext context)
			throws Exception {
		ActionBean result = container.getComponent(type);
		return result;
	}
}

Inside of the ActionBean you just use the service methods.

@DefaultHandler
	public Resolution index(){
		context.getMessages().add(new SimpleMessage(sampleService.hello()));
		return new ForwardResolution("/pico.jsp");
	}

All our ActionBean classes only need to define a constructor with all services they need. We add the services to the container and get a ready to run ActionBean.

h2. Dependencies and libraries

Stripes tries to minimize the dependencies. As a consequence there are only three required libraries with a total size of about 600 K

The commons-logging.jar is a frequently used logging abstraction but at the same time well known candidate for class loading issues. It would be nice, if Stripes could migrate to the slf4j logging facade as many other frameworks has already done. But anyway, the short list of dependencies is a strong advantage.

h2. Flow of development

A new dialog requires a new ActionBean, entries in a resource bundle and new JSP pages. The Stripes reloading extension allows to reload all this without restarting your application. You will still need to reload the web application, if you add a new domain model or business service.

h2. Popularity and contributor distribution

Google pages on stripes framework: > 1,100,000

Books on Stripes: 1

Number of mails on mailing list per month: 220

Number of core developer: 3

Number of regular patcher/contributors: none

h2. My impression of the framework

Stripes is an easy to learn and well structured web framework. It is action oriented and follows the model-view-controller pattern. The advantage to follow this action approach is that it is easy to understand and supports perfectly the underlying technology, i.e. HTTP. The web and HTTP are action / request oriented. A request is sent, logic is processed and depending on the result, the browser gets an error message or a response, which might be a HTML page or a redirect.

The disadvantage of this approach is that you are closely connected to the underlying technology. Frameworks that clearly abstract from the servlet API, for example Tapestry, shields you from the caveats of the technology and can provide you with useful additional functionality. I will try to back this with an example.

A input tag has a corresponding component class in Tapestry. I can add behavior to this tag using mixins. If I would like to have all input tags for date values have a nice Javascript date selector, I add a mixin for date values. If I want to change the date selector application wide, I need to change only a single place in my Java code and not all JSPs.

Of course, the abstraction and the provided additional features have the tradeoff of complexity. I am not valuing that one approach is better than the other, but try to make you aware of the consequences.

Going back to Stripes.

Stripes makes use of conventions to facilitate the development and to keep the developer from writing boiler plate code. Configuration with XML is only required, if you want to change the default components or Stripes or add interceptors. Instead of using XML you have the option to use a configuration class.

What I appreciated a lot is the architecture. It is pretty easy to understand, follows a clear concept and offers hooks to add custom behavior to the execution chain. The 'How do I do' part and the related sample projects illustrates how easy it is to add a custom security solution or even integrate a Inversion of Control container.

A weak point is the small number of developers and contributors. The bus rate (people who can be overrun by a bus) is relative bad. Stripes is well known but by far not as popular as frameworks like JSF, Tapestry or Struts. As a consequence, you will only find very little developers with Stripes experience. This is mostly outbalanced by the fact that, the learning curve is very flat and a new user is capable to build dialogs after one or two days of learning Stripes.

Stripes provides you with the minimum you need to do a web application. It doesn't come with a large collection of input widgets, security or even an integrated persistence layer. You are free to choose your own technologies to fulfill those requirements and Stripes offer you the hooks to integrate your stuff.

A weak area, is in my opinion the Ajax support. If you want to synchronize Ajax calls to session scoped variables, do client side validation using Javascript or need proper Ajax aware exception handling, you will always have to implement your own solution. This would be fine, if Stripes provides a very good documentation on all those topics but documentation is rather short. Ajax is more than generating JSON from an event method. This is an area where Stripes need to catch up.

The documentation is fine and covers all areas of Stripes but the structure could be improved. After a quickstart chapter, it looks like a wild collection of howtos which are partially translated to French.

To summarize, if you are looking for an easy to learn web framework with a well designed architecture and you want create your dialogs based on HTML and use external Ajax frameworks for more advanced input widgets, then Stripes is the right choice for you. If you are looking for a replacement for Struts 1.x, then Stripes is the perfect candidate. You will get along with Stripes easily and find that all the annoyances of Struts 1.x are solved.

If you are looking for something that clearly abstract from the underlying technology, providing you Ajax components for dialog development and providing ready to use functionality for security, workflows etc, then you might look for something else.

The community's opinion - beautiful features and concepts

I asked the following question on the mailing list:

What is your favorite feature or concept of Stripes, what is unique as compared to other things, what is an eye catcher?

Here are extracts ore rephrased parts of the feedbacks:

Freddy Daoud

What caught my eye about Stripes is that you can get a lot done without configuration nor boilerplate code. Also, to me it is an excellent balance of convention over configuration but without *too much* magic going on - it remains simple and straightforward.

The bottom line is that I found that I could get more done with less code with Stripes, and that it gave me the confidence that I would be able to get it to do whatever I needed. I wouldn't have to tell my customer that I couldn't implement a feature because the framework got in my way.

Oscar Westra van holthe - Kind

Conversion, validation, error handling, etc. is done very well. As a result, the ActionBean only sees validated values, without making custom validation too difficult.

Also, the type converters that can automatically load data objects from the database (like for example with Stripersist) are extremely powerful.

The life cycle and the interceptors make it possible to create all kinds of neat extensions. One of them is Stripersist, but security, authentication, logging, ... it's all possible. It resembles aspect oriented programming, but without breaking the basic assumptions one usually makes when workign with a programming language.

Yee

Indexed properties is a great feature. (Author: it allows table based multi row input ). It was WOW when I first saw it. Stripes is simply beautify. Everything is nice and lean and perfectly placed. There is not an ounce of unnecessary fat in it.

Morten Matras

The one thing that separates Stripes from other frameworks is the learning curve, which is very flat compared to other frameworks.

....

When I add new developers to teams working with Stripes it normally takes them less than 2 working days before they have added a new feature or enhancement to the product and all they need to get started is source code and an IDE to be set up.

Remi

It's simple, it does what you need (binding, first class FORM/validation support, extensibility etc.) without ever getting in your way, just like you'd have imagined it should be!

John W Newman

I have been pleased with the code activity and the quality of the commits, the project is always improving despite being small and close-knit (this is one of the best qualities of the project, more committers = more confusion and senseless feature bloat).

As far as I am concerned, the only thing stripes isn't perfect with now is ajax events. We (and probably everyone else) were forced to write our own set of glue code to make ajax work correctly with validation errors etc. Fortunately prototype 1.6 and the x-json header made this pretty simple, but I think the web framework could take responsibility and make this easier on everyone. This is a shortcoming and I'm not sure if there's any motivation right now to address it. Cleaner ajax event support should be thoroughly discussed.

Also I'd like to move all of our @Validate tags to fields in the entity beans where they belong. I know some work has been done on this and maybe we can have it as a core feature in a future release. I think after the JSR on a standard validator thing is released, stripes should take that and run with it (assuming they don't totally botch it like they did with type erasure).

h1. Features h2. Render / page technologies

Stripes supports two technologies to create pages: JSP and Freemarker templates. It comes with a tag library which can be used with both rendering technologies. The Stripes tag library is a thin layer around HTML tags.

This does of course not prevent you from using other tag libraries. It is actually surprising that a lot of companies don't write their own tag library which renders a number of components at a time, sample: to render label, input and validation errors.

h2. Developer comfort

There is a plugin to allow reloading of resource bundles, ActionBeans and converters. As long as you don't change your business layer or model, you don't have to reload your application. This improves development performance a lot.

http://www.stripesbook.com/stripes-reload.html

h2. Ajax support

Stripes allows to take control over the response to render XML, JSON or whatever you like to send as answer to a Ajax request. In addition it provides a basic level of remoting. You can transform a Java object to a JSON form and rebuild it on the client as JavaScript object.

The tag libraries do not provide Ajax support but you can add your preferred JavaScript library to build Ajax application. In my opinion, this is the preferred approach as Ajax tag libraries like Icefaces, provide basic Ajax functionality and components and are more complex to adapt to advanced use cases.

There will be a number of things you would have to implement on your own. One thing is serializing concurrent access to methods. There is always a chance that Ajax requests are send multiple times and it is your responsibility to serialize those calls if required.

You can provide an exception handler in Stripes. Inside of this exception handler you need to distinguish between Ajax calls and normal requests. If you hit an error, you will probably show a friendly error page to the user. An Ajax call needs to receive an real HTTP error message like 500 or 404 if something failed. You have to implement this inside of your exception handler and make sure that the Ajax calls signal for example with a request parameter that it is an Ajax call.

h2. Security

There is no security concept provided by Stripes itself but you can find two contributions showing how to solve the problem.

The first one offers to do a per method authorization using annotations and offers a tag library to be used inside of a JSP. The contribution was written for an older Stripes version and is to basic to be used in real world cases. The tag library contains only one tag to hide a part of the JSP if a role is not available. A suitable tag library should at least offer the following use cases:

This could be done using multiple tags or a single tag using functions. Here a sample API with functions:


	shown if you have role foo or bar


	shown if you have none of the roles foo or bar


	shown if you have role foo and bar

The same conceptual limitations are true for the annotations provided by this contributions.

The second contribution integrates EJB3 security and makes use of the JEE annotations. The idea is nice but of course ties you to EJB3 libraries. The tag library allows to hide part of a JSP depending on the configured roles for a method of the referenced ActionBean. I understood the user case � showing a link only if I am allowed to follow this link � but still it is a kind of specialized use case.

To summarize, I think that the offered security is not enough for a larger application. Therefore you either use an external Security Framework or write your own security solution. This is not a complicated task, a little tag library, a couple of annotations and the logic to authenticate and authorize. It shouldn't take longer than a couple of days. The advantage of Stripes is, that it is easy to integrate security into the request processing.

h1. How do I do ...?

The howto project provided with the sources illustrates all the functionality explained in this chapter.

h2. Navigation

The stripes tags are a light abstraction on top of HTML. The link just add the context to the attribute value of href. The following link calls the default action of the HelloWorld.

Go to hello world

The next link calls the save method of the HelloWorld class.


	Go to hello world

The form tag offers the same functionality.


Events can be specified in the submit button, as well. The following button will execute the save method.

		Write to database

The default URL is taken from the ActionBean class name. If you use a package name like

de.foo.bar.HelloWorld, the URL to execute the class is /de/foo/bar/HelloWorld.action. If you add one of www, web, stripes or action to the path, the path starts from there.

Use de.foo.bar.web.HelloWorld to get a mapped URL like /HelloWorld.action.

You can overwrite the default URL mapping.

@UrlBinding("/HuhuWorld.action")
public class HelloWorld implements ActionBean{

Friendly URLs

Friendly URLs express that the user things 'Oh, that's a nice website, the URLs are locking friendly' and that search engines think 'Oh, that's a nice website, I can index it'.

Friendly URLs can be configured with the @URLbinding annotation.

The following binding will write xx into the foo attribute and yy into the bar attribute of the class.

Request: /action/Test/xx/yy/myevent

@UrlBinding("/action/Test/{foo}/{bar}/{$event}")
public class HelloWorld implements ActionBean{
	private ActionBeanContext context;

	private String foo;
	private String bar;

h2. Templates

The first step is to define a template with placeholders. The tag defines a placeholder.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="stripes" uri="http://stripes.sourceforge.net/stripes.tld" %>

    
        
            Layout Example
            
        
        
            
                
            

            

A page can reference the template and define content for the placeholders.

<%@ taglib prefix="stripes" uri="http://stripes.sourceforge.net/stripes.tld" %>

    
        Hello World!
    

In addition you can pass variable to a template, used nested layouts or use a template just for a page fragment. The templating mechanism is simple but well done.

h2. Forms and validation

The first step is to provide variables for the input. Inside of the ActionBean you add properties with public getter and setter.

public class FormsAndInput implements ActionBean{
	private ActionBeanContext context;
	private String foo;
	private Date date;

public String getFoo() {
		return foo;
	}
	public void setFoo(String foo) {
		this.foo = foo;
	}
	public Date getDate() {
		return date;
	}
	public void setDate(Date date) {
		this.date = date;
	}
.....

Then you add a method forwarding to the form and a method to handle the submit.

@DefaultHandler
	public Resolution index(){
		return new ForwardResolution("/input.jsp");
	}

	public Resolution save(){
// we just print the input
		System.out.println(String.format("Date: %snFoo: %sn", date, foo));
		return new RedirectResolution("/input.jsp");
	}

Finally you create the form in the input.jsp

<%@ page import="java.util.Date" %>
<%@ taglib prefix="s" uri="http://stripes.sourceforge.net/stripes.tld" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

Simple jsp page



	
Foo
Date <% request.setAttribute("date", new Date()); %> Input sample:
Save me

The final step is to add validation. Out of the box, Stripes only offers server side validation. This fulfills security requirements but does not offer the user friendly client side validation. You might select a Javascript based validation engine to complete this task. Stripes provides access to the validation annotation inside of the JSP. You can use this information to create the Javascript validation generically from this information. But now let's see what can be done already with Stripes.

There are three levels of validation

Basic validations are defined as annotation to the fields of an ActionBean. You can define checks like length, size, required and masks.

@Validate(required = true, minlength = 2)
	private String foo;

@Validate(field="zip", required=true, mask="\d{5}(-\d{4})?")
private String zip;

Custom validation

The next step is to define a custom validation method. There can be multiple of them, they can be sorted and you can specify if they should be executed, if an error has already happened.

	@ValidationMethod()
	public void validate() {
		if (foo.equals("bad")) {
			ValidationErrors errors = new ValidationErrors();
			errors.add("foo", new SimpleError("Foos balue is bad"));
			context.setValidationErrors(errors);
		}
	}

The validation is flexible and powerful. I can only imagine a simple improvement. I would like to define a custom validator inside of @Validate. Every business application has frequently used types that are specific to that company. Instead of writing a validation method everywhere, the following would be simpler and shorter.

@Validate(custom=de.laliluna.EmailValidator.class)
private String email;
@Validate(custom=de.laliluna.ArticleNumberValidator.class)
private String articleNumber;

Advanced converting

Enums and dates are properly parsed, but time and date+time converts are missing.

Customize converting of input

You can specify custom converters for every field. A custom converts just needs to implement the interface TypeConverter.

@Validate(required = true, minlength = 2, converter = FooConverter.class)
	private String foo;

In addition you can replace the converter factory with your custom implementation to provide your own converter for all java.util.Date for example.

h2. Common dialog tasks

Are more complex input widgets provided like calendars or tree tables.

Stripes provides only the basic HTML like input elements. But you can add Javascript based component libraries like jQuery UI, Yahoo etc. Have a look at the main article for further references.

Is there a simple way to iterate through collections?

You can use the JSTL tag library to do this task. Stripes does not mirror its functionality. The Freemarker templates have their own mechanism to provide loops.

JSTL sample


	
		
			${person.id}
		
		${person.username}
	

How do I hide a part of the page depending on a condition?

You can use JSTL to achieve this.

bla bla

How do I print a sorting table?

Stripes does not include more complex page elements. You might use the well know displaytag library or make use of a Javascript widget collection. Both provide you with powerful complex page elements.

Multi row input and indexed properties

Stripes has support for multi row input. You create a form and used index properties for input. The following JSP shows existing persons and adds an input row for new persons.


			
		
		

Rendered HTML

The ActionBean class has the following field to store the persons.

	private List persons = new ArrayList();

h2. Message resources and internationalization

Stripes makes use of Java resource bundles and places text resources for a language into a single property file. The file is postfixed with the language and/or the country.

StripesResources_en_US.properties

Resource bundles cannot be modularized which is quite inconvenient for large applications. The only way to change this is to replace the bundle factory of the configuration and provide your own mechanism to load the resource bundles

Reloading of resource bundles during development is possible with the reload plugin named in chapter 2.2.

h2. Exception handling

You can provide your own exception handler. It is called from the servlet filter. This allows to handle an exception even if a JSP is called directly. You can get retrieve the ActionBean, if one exist already, from the request. This provides access to the ActionBeanContext, which has all relevant information.

public class MyExceptionHandler extends DefaultExceptionHandler{
	/**
	 * An exception handler to handle RuntimeExceptions
	 */
	public Resolution handle(RuntimeException e, HttpServletRequest request, HttpServletResponse response){

// clean up, send HTTP error code for Ajax request 
// or redirect to an error page
		return new ForwardResolution("/error.jsp");
	}
}

h2. Redirect on post and flash scope

You have full control over the dispatch mechanism by returning a Resolution from an event method. There are a number of predefined resolutions to forward, redirect or stream data but you could create your own resolution as well.

return new ForwardResolution("/error.jsp");
// or 
return new RedirectResolution("/edit.jsp");

A flash scope is implemented using an id which is added to URL in case of a redirect.

http://localhost:8080/stripes/edit.jsp?__fsk=-556973262

h2. Conversation context, wizards and workflows

Stripes offers a simple mechanism to carry input fields from one page to another. It encrypts input fields from the first page and prints them encrypted as single hidden input on the next page. The advantage is that you don't have to fill up your session. The disadvantage is that this hidden field might become quite large.

There is no support for a conversation context or workflows. Of course you can plugin your own mechanism.

h2. Double submit handling

There is no direct support for double submits. A possible manually implemented solution could look like the following:

	@After(stages = LifecycleStage.CustomValidation)
	public Resolution doubleSubmit() {..}
h1. Performance and scalability

This chapter is included in the PDF document framework-evaluation-performance.pdf.

h2. Caching

h2. Infrastructure

I set up an infrastructure which guaranties that the client and the network is never saturated. As a full server is to fast to be tested with only one notebook as client, a virtual server was setup which uses only one CPU.

h2. Startup and reloading

The sample application contains 50 dialogs offering list, edit, delete, create of an item. In a real world application you need to add the time to initialize the business and the persistence layer.

Startup time

Reloading time after changes

h2. Performance test h3. Get request of a static file

The test is used to provide a base to find out the processing time of the web framework. The following formula explains this:

Processing time = GET request to web framework � GET request of static file

Scenario

h3. Get request and rendering performance

Scenario

h3. Get request and template rendering performance

Scenario is repeated but the rendered page uses a template

h3. Post request and validation performance

Scenario

Static file

GET

GET with template

POST

Average time of all request

Average time of 90 % fastest request

Average time for 1000 slowest request

Average memory consumption (GC log)

Max memory consumption (GC log)

Memory start/end (after GC) (GC log)

Total collected memory during garbage collection (GC log)

Memory usage Heap and Non Heap after a Full GC at the end (JVM info)

h3. Max scalability for given hardware

Scenario