Controllers

Controllers are classes which receive the requests and perform the required operations using the models. Controllers in EasyWeb4J applications must extend the Controller class. The package containing the controllers are specified by the CONTROLLERS_PACKAGE init parameter of the DispatcherFilter. The default value for this parameter is project_package_parent.controllers (for example, org.languages.controllers). The name of controller classes must always end with "Controller". The root controller which is used when no controller has been specified is determined by the ROOT_CONTROLLER init parameter of the DispatcherFilter.

A controller in an EasyWeb4J application contains methods with the signature public void action_name(). Each method represents a specific action within the controller. Actions are the methods which process specific HTTP requests. For example, the below mentioned URL will be handled by the public void add() method of ParadigmsController class in org.languages.controllers package.

http://localhost:8080/languages/Paradigms/add

Simple Request Filter

Controllers can override the filterRequest() method to filter requests before the corresponding action is executed. If the filter redirects or renders a response or directly writes to the HttpServletResponse, the action will not be executed.

This filtering mechanism can be used to implement features such as authorization in the web application.

Parameters in Action Methods

EasyWeb4J allows action methods in controllers to have parameters whose values are provided from the components in the request URL. The parameters can be of any of the types supported by updateModel() method (as listed below).

http://localhost:8080/contextRoot/Users/profile/1/abc

In the above URL, 1 and abc will be converted into int and String respectively and passed as parameters to the action method shown below.

public class UsersController extends Controller {
    public void profile(int userId, String userName) {
    }
}

The action method to be invoked is decided based on the number of parameters. When more than one method have the same number of arguments as provided in the URL, the first method in the order of declaration is used.

Updating Models' Properties

EasyWeb4J provides the updateModel() method. This method takes a model to update, an object paramter map and an AssociationsProvider instance as parameters.

There is also a variant of this method which does not take an AssociationsProvider. It can be used for updating those models which do not have any association.

Extracting the Properties from Request Parameters

EasyWeb4J uses the names of the request parameters to group properties of the same model. updateModel() method must be used to extract the map of property values to be passed to the model.

The updateModel() method, when invoked, performs the following sequence of operations.

  • Make a copy of the model passed.
  • Clear all current errors in the new copy.
  • Update the values of fields which are of valid format. Current values of invalid fields are held as String internally which ModelELResolver would pick for displaying in the view.
  • Invoke the validate() method to perform validations on the model.
  • If the updated model is invalid, it returns the copy of the model else returns the original model.

The isValid() method can be used to determine whether a model instance is currently in a valid state or not.

Assuming that the HTTP request has the following parameters,

language.name ["Scala"]
language.paradigms ["2", "3"]
language.executionEnviroment ["2"]
some_other_param ["some_value"]

Invoking updateModel(new Language(), "language", provider) from an action method would update the model with values as shown below.

name ["Scala"]
paradigms ["2", "3"]
executionEnviroment ["2"]

The AssociationsProvider

The updateModel() method automatically handles the conversion of the request parameters to the below mentioned types.

  • String
  • Date
  • Character/char
  • Boolean/boolean
  • Byte/byte
  • Short/short
  • Integer/int
  • Long/long
  • Float/float
  • Double/double
  • BigDecimal
  • BigInteger

For properties of other types (including assocations/collections) developer must provide an instance of an implementation of the AssociationsProvider interface. This interface contains the getAssociation() method which would be invoked by updateModel() method to obtain the value for properties of types other than the ones mentioned above. The name of the property and the request parameters available for that property are passed to this method.

For example, in the Language example above, this method would be invoked twice as shown below,

  • getAssociation("paradigms", ["2", "3"])
  • getAssociation("executionEnviroment" | ["2"])

Controlling Navigation

Unlike many other Java web frameworks, which maintain navigation configuration in configuration files (mostly XML), EasyWeb4J allows you to specify navigations in plain java code. This along with the fact that EasyWeb4J encourages thin controllers, ensures easily maintainable navigations even in large applications.

Rendering the Default View

This is the simplest form of navigation and requires no work from the side of the developer. When developer does not specify a navigation explicitly, the default view for the request's action is rendered. The default view for an action is determined by the name of the controller and the action handling the request.

For a request which is handled by the add action of ParadigmsController, the default view would be,

/WEB-INF/views/Paradigms/add.jsp

Rendering a Different View

In case where the developer would like to render a view different from the default view for the action, he can specify the alternate view using the render() method.

A sample usage within ParadigmsController and the corresponding view rendered are shown below.

render("otherView");

View Rendered: /WEB-INF/views/Paradigms/otherView.jsp

Note that the name of the view rendered does not include the .jsp extension.

Rendering Without Layout

In the above two ways of rendering a view the layout of the pages are also rendered. More details about working of layouts will be discussed in the next section. For now, it is sufficient to understand that layout is a page's template.

In certain cases (response to AJAX requests), it might be undesirable to include the layout of the page. In such cases you can use the renderWithoutLayout() method to render a specific view without its layout. It works similar to the render() method, except for the fact that it does not include the view's layout.

Redirecting

Redirection is useful for implementing the PRG pattern and in use cases like authorization. In EasyWeb4J implementing redirection is as simple as invoking the redirect method with appropriate parameters. Here are the various forms of the method.

  • redirect(controller, action, id)
  • redirect(controller, action)
  • redirect(action)
  • redirect(Controller)

For the last two variants, action and controller are determined based on the case of the first letter in the value. If the character is upper case, the index action of the specified controller is used. The specified action is used if the first character is lower case.

Generating Raw Response

In certain cases you might be required to send output to the response's OutputStream directly (reports, user provided images, etc). Writing such output directly to the servlet's reponse from the controller is not recommended as it will be performed during the action transaction and not during the view rendering transaction. This can lead to the caveat described at the end of this section.

EasyWeb4J provides the ResponseHandler interface to handle raw responses cleanly. Anonymous implementations of this interface can be passed to the render() method, which will be used it as a callback to generate the response during the view rendering transaction.

Sample Usage:

public class HomeController extends Controller {
    public void process() {
        render(new ResponseHandler() {
            @Override
            public void handleResponse() throws Exception {
                // Sending redirect to a static page
                getResponse().sendRedirect(getServletContext().getContextPath() + "/static.html");
            }
        });
    }
}

Flash Messages

Another challenge faced while implementing PRG pattern is storing messages to be displayed to the user. Let's consider the following scenario.

  • Post information to create a new record.
  • Save the Record and set a success message.
  • Redirect to page listing the new set of records.
  • Display new set of records along with the success message.

If the success message is stored as a request attribute it will be lost during the redirect. If the message is stored in the session it will live much longer than needed.

EasyWeb4J provides Flash Messages to avoid this. Flash Messages are retained for exactly one request after the request in which they were set. Flash messages can be set using the setFlash() method as shown below.

setFlash("paradigmSuccess", "Paradigm Saved Successfully!");

This can later be used in the view pages using the EL expression, $flash['paradigmSuccess'].

Handling Exceptions

As it can be seen in the request processing section, action methods are invoked within an EasyWeb4J managed transaction. The transaction is rolled back if the invocation of action method threw an exception. If an action handles exceptions occurring during its execution within itself, Dispatcher will not be aware of it and hence will not roll back the transaction. Hence EasyWeb4J provides controllers with a callback method, handleActionException(), which is passed the exception which occurred in the action after the transaction has been rolled back. This method can be overridden to handle exceptions after transaction is properly rolled back.

So the recommended practice for handling exceptions in action methods would be,

  • Handle exceptions which should not cause a transaction roll back within the action method.
  • Do not handle exceptions which should cause a transaction roll back within the action method. Use the handleActionException() callback instead.