Download pulse

Using RequestBeans - elegant request evaluation

You have already learned how easy it is to expose a web method in pulse using the @Action annotation. Working with the parameters of a request is just as simple: using RequestBeans helps keeping your business logic clean and readable.

The general idea behind RequestBeans

Evaluating a request can sometimes be very tedious. All the working with Strings clutters your code with redundant checks, makes it difficult to read and even more difficult to refactor.

This is why we created RequestBeans for pulse:

  • separating input parsing and validation from business logic
  • making parameter access truly object oriented
  • resulting in very readable code
  • enabling easy refactoring in your IDE

RequestBeans are used for annotation based processing of the Parameters of a Command. A parameter of a method annotated with either @Action or @AnyAction that is annotated with @RequestBean can have values injected based on Command.getParameters(). The injection itself is controlled by the @RequestBean.Parameter annotation.

The package org.torweg.pulse.util.requestbean already contains some useful base implementations: RequestAwareRequestBean (which gives access to the underlying ServiceRequest), BundleAwareRequestBean which gives access to the current Bundle) and ContextAwareRequestBean which gives access to the underlying ServiceRequest and the current Bundle). If you need to access the ServiceRequest or the Bundle within your RequestBean, you can do so using the @RequestBean.ServiceRequest and @RequestBean.Bundle annotations.

Changing UserListController to make use of RequestBeans

For our example we are changing UserListController from the extended controller example to make use of RequestBeans.

From ServiceRequest to RequestAwareRequestBean

If we are using the predefined RequestAwareRequestBean first, changing UserListController couldn't be easier:

@Action("downloadExcel")
public final void downloadUsersExcel(
		final @RequestBean RequestAwareRequestBean requestBean) {

...		
				
		// add event to EventManager		
		EventManager eventManager = requestBean.getServiceRequest()
				.getEventManager();
...
}

Simply change ServiceRequest to RequestAwareRequestBean and adapt the line which retrieves the EventManager.

A custom RequestBean

Any ordinary Java class can become a RequestBean. Methods annotated with a sub-annotation of @RequestBean can have Parameters, ServiceRequest and current Bundle injected into methods and fields. For the sake of simplicity we will start off by extending one of the predefined base implementations:

package org.torweg.pulse.component.example;

import org.torweg.pulse.util.requestbean.RequestAwareRequestBean;


public class UserListRequestBean extends RequestAwareRequestBean {

}

And change the UserListController to reflect the latest changes:

@Action("downloadExcel")
public final void downloadUsersExcel(
		final @RequestBean UserListRequestBean requestBean) {
...
}

Putting it all together - filtering users by e-mail address

For our example, we will create a RequestBean which encapsulates a request parameter. The value of the request parameter will be used to select only users with matching e-mail addresses for inclusion in the Excel file.

Now creating a RequestBean which takes the value of the request parameter "email" as a String can be as simple as:

public class SimpleRequestBean {             
        @RequestBean.Parameter("email")
	public final String emailFragment;
}

We will make our RequestBean just a little more clever and elaborate. The code below should be self-explaining:

public class UserListRequestBean extends RequestAwareRequestBean {

	private String emailFragment;
	
	@RequestBean.Parameter("email")
	public final void setEmailFragment(final String f) {
		this.emailFragment = f;
	}
	
	public final boolean containsEmailFragment() {
		return (this.emailFragment != null);
	}
	
	public final String getEmailFragment() {
		return this.emailFragment;
	}
}

If the request contains a parameter "email", its value will be set using setEmailFragment(String). A corresponding getter method is also present and the method containsEmailFragment provides a convenient and readable check method, whether the parameter is set.

Remember, RequestBeans are fully fledged classes so you can make them arbitrarily clever.

Adding the additional logic to honour the "email" parameter - if present - becomes very simple. Accessing request parameters via the RequestBean keeps the business logic clean and adds to its readability.

@Action("downloadExcel")
public final void downloadUsersExcel(
		final @RequestBean UserListRequestBean requestBean) {
    Session s = Lifecycle.getHibernateDataSource().createNewSession();
    Transaction tx = s.beginTransaction();
    try {

        Criteria criteria = s.createCriteria(User.class);
        if (requestBean.containsEmailFragment()) {
            criteria.add(Restrictions.ilike("email",
                    requestBean.getEmailFragment(), MatchMode.ANYWHERE));
        }
        @SuppressWarnings("unchecked")
        List<User> users = criteria.list();
        LOGGER.info("{} user(s) found", users.size());

...    

        tx.commit();
    } catch (Exception e) {
        tx.rollback();
        throw new PulseException("Error: " + e.getLocalizedMessage(), e);
    } finally {
        s.close();
    }
}

To test your code go to:

http://localhost/pulse/Pulsar/.Example.downloadExcel../?email=fragment

As you can see the resulting code is still straight forward and the handling of request parameters is encapsulated in its own class: First a simple criteria requesting all users is created. If the RequestBean contains an e-mail fragment a restriction is added to the criteria, requiring the users' email addresses to contain the fragment.

How are RequestBeans processed?

Whenever a web method is called by the pulse container, all its RequestBean parameters are processed. A field or method annotated with @RequestBean.Parameter will be injected with the value, if and only if the named parameter is present.

Each method or field annotated with @RequestBean.Parameter must adhere to the following contract:

The acceptable types are: String, String[], Collection<String>, Set<String>, List<String> or Parameter.

Moreover a RequestBean may contain an arbitrary number of methods and fields annotated with @RequestBean.ServiceRequest or @RequestBean.Bundle.