Getting started

Creating the webapp

The easy way

Download the latest doff's jar and type this command from a terminal:

$ java -jar doff-1.5.1.jar -g MyWebApp
create          .../MyWebApp/                           OK
download        build.xml                               OK
download        jsp-api-2.0.jar                         OK
download        servlet-api-2.4.jar                     OK
create          .../MyWebApp/doc/                       OK
create          .../MyWebApp/doc/javadoc/               OK
download        c.tld                                   OK
download        doff.tld                                OK
download        fn.tld                                  OK
download        fmt.tld                                 OK
download        doff-1.5.1.jar                          OK
...

That's all, you now have a webapp configured! If a problem occurs, check your internet connection.

The manual way

We assume that the webapp context has already been configured.

First, create your webapp directories tree as other JEE webapps. It will look like this:

WEB-INF
|
|-- classes
|-- lib
`-- web.xml

Then, download the doff's jar and put it into the WEB-INF/lib directory. Log4j and Commons FileUpload (at least version 1.1.1) libraries are needed too. Both of those libraries are in the full Doff package.

Next, the only configurution you need to do is on the web.xml file. This file will contains those lines:

<servlet>
    <servlet-name>main</servlet-name>
    <servlet-class>org.alweb.doff.Servlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>main</servlet-name>
    <url-pattern>/do/*</url-pattern>
</servlet-mapping>

Note that you can change the prefix of the main servlet mapping (Here: /doand put it into the load-on-startup element isn't needed but it will configure all Doff controllers and filters when the servlet is loaded.

You can download a web.xml file already configured.

Your first controller

Now, it's time to create your first controller. All your controllers must extends Controller class. A controller contains actions. Actions are just class methods annotated by @Action. Here is an example:

package com.example;

import org.alweb.doff.annotation.Action;
import org.alweb.doff.core.Controller;

public final class Home
extends Controller
{

    @Action
    protected void index()
    {
        println("Hello World!");
    }

}

This will display Hello World! from URL .../do/home. That's all, there is no configuration or mapping declaration to do. Doff will search Controller's subclasses automatically. Only concrete (not abstract) classes will be considered.

By default, actions used GET HTTP method is used for actions, we will see in the next chapter how to change it.

URL path matching controller Home is determined from the name of the class but all uppercase letters of the class name are preceded by a slash (/) and converted to lowercase.

Action methods called index will always match the root path of the controller. Otherwise, action will match the controller path concatened to the method name (converted like controller's path). We will see actions in details in the next chapter.

/**
 * Here, the path of the controller is: /users/groups.
 */
public class UsersGroups
extends Controller
{

    /**
     * The path of this action is /display. You can invoke this action from
     * URL .../do/users/groups/display.
     */
    @Action
    protected void display()
    {
        println(getAction() + " action invoked");
    }

}

In many cases, you don't want the controller's class name as URL path. You can change it thanks to @URLPattern annotation. This annotation can also determine if URL is case-sensitive or not. By default path is case-sensitive.

All non-static fields of your controller are automatically added to the current request before rendering view.

Let's modify the previous controller:

/**
 * The path of the controller here is /groups. 
 */
@URLPattern(path = "/groups", caseSensitive = false)
public class UsersGroups
extends Controller
{

    /**
     * Matches .../do/groups/display and even .../do/Groups/DISPLAY,...
     */
    @Action
    protected void display()
    {
        println(getAction() + " action invoked");
    }

}

About actions

We will see how to create actions and all of their features. Actions are just controller's methods annotated by @Action. Methods can be private, protected or public.

First, you can specify the HTTP method used for the action. HTTP method can be DELETE, GET, OPTIONS, POST, PUT or TRACE. The default method is GET.

The HEAD method is voluntary not present because the metainformation contained in the HTTP headers in response to a HEAD request should be identical to the information sent in response to a GET request.

Here is an example with POST HTTP method:

@Action(method = Method.POST)
protected void add()
{
    println("Post action");
}

HTML doesn't supports links and forms using others methods than GET and POST. That's why doff provides (like Prototype) an extra parameter « _method » to override the method to use. Thanks to this parameter you can use PUT, DELETE,... methods. The tag methodInput provides a way to create an hidden input with the method of your choice for your forms :

<doff:methodInput method="delete" />

<input type="hidden" name="_method" value="DELETE" />

Then, you can specify the path of the action (the path is relative to the controller's path):

@Action(method = Method.POST, path = "/foo")
protected void add()
{
    println("Post action");
}

By default, the path is determined from the name of the method but all uppercase letters of the class name are preceded by a slash (/) and converted to lowercase.

The action's path can contain parameters. Those parameters can be:

Note that parameter must exists (not be the empty string) in order to execute the action.

Then, parameters can be obtained thanks to getPathParam() method. You can specify the index of the path parameter to this method (index starts at 0). If you don't specify it, the first parameter is used.

/**
 * Try it from URL .../release/doff/v42
 */
@Action(path = "/release/${string}/v${int}")
protected void hello()
{
    println("Release: " + getPathParam().toString());
    println("Version: " + getPathParam(1).toInt());
}

If you want to obtain parameters from method's parameters, take a look to this chapter for more informations.

Finally, if you want to match the root path of the controller's you can set the index property of the @Action annotation to true or call you action's method « index ». If an action is an index, its path will be ignored.

@Action(index = true)
protected void list()
{
    println("Hello World!");
}

Some useful methods

Doff provides many useful methods in Service class. This class is inherited by Controller and Filter classes, so you can use those methods in your own controllers and filters.

I suggest you to take a look to the javadoc of Service class for more informations.

There is many other methods, take a look to the javadoc!

And what about filters

Filters are used for executing tasks before and/or after all actions. The rule is sensibly the same as javax.servlet.Filter interface but Doff adds some features.

Like controllers, you don't need to write any configuration file for declaring your filter. You only need to extends the abstract class Filter and implements the method execute.

The execute method takes an ExecutionChain as parameter. This parameter is used for shifting the execution to the action and/or to other filters.

Here is simple filter examples:

public final class HelloWorldFilter
extends Filter
{

    /**
     * The message is displayed to the standard output AFTER the execution of
     * the action.
     */
    @Override
    public void execute(final ExecutionChain chain)
    {
        chain.next();
        System.out.println("Hello World!");
    }

}
public final class HelloWorldFilter
extends Filter
{

    /**
     * The message is displayed to the standard output BEFORE the execution of
     * the action.
     */
    @Override
    public void execute(final ExecutionChain chain)
    {
        System.out.println("Hello World!");
        chain.next();
    }

}

Don't forget to invoke chain.next(), otherwise, the action or other filters will not be executed.

By default, filters are executed when an action matches current URL. If you want to execute your filter even if no action can be found, you need to annotate your filter by @ExecuteIfNotFound.

@ExecuteIfNotFound
public final class HelloWorldFilter
{
    ...
}

Dependencies between filters can be specified thanks to @Depends annotation. This annotation takes an array of Filter subclasses as value. All specified filters will be also executed before annotated filter.

@Depends({ MyFilter.class, OtherFilter.class, })
public final class HelloWorldFilter
extends Filter
{
    ...
}

You can exclude controllers of your filter (useful for authentication filters). You only need to annotate your filter by @Exclude and add an array of excluded controllers as value.

@Exclude({ Downloads.class, Overview.class, })
public final class HelloWorldFilter
extends Filter
{
    ...
}

You can also exclude some filters only for an action:

@Action(exclude = { AuthenticationFilter.class, CheckOwnerStatus.class, })
protected void login()
{
    ...
}

You can restrict filter execution on some controllers. You just need to annotate your filter by @OnlyFor. Then, specify controller's classes array.

@OnlyFor({ Documentation.class, Tutorial.class, })
public final class HelloWorldFilter
extends Filter
{
    ...
}

Obtaining parameters

With Doff, obtaining request parameters and converting them to usual types is very easy.

/**
 * Returns the parameter "id" to int format or 0 if malformed or missing.
 */
getParam("id").toInt();

You can convert a parameter to boolean, string, integer, double or long types (methods toBoolean, toString,...). All of those methods can take a default value (returned if the parameter is malformed or missing). Here is an example:

/**
 * Returns the parameter "validate" or true if malformed or missing.
 */
getParam("validate").toBoolean(true);

For obtaining multiple parameters (useful with multiple selects), there is a method called getParams, which takes the name of the request parameter as parameter and returns a collection of parameters.

If you want to obtain all request parameters, just use getParams method with no parameters. This method returns a map of parameters as values and parameters names as keys.

You can add parameters to your action's method. By default, they will have the value of path parameters, in the same order. Note that if you declare a parameter that doesn't exists in action's path, a MappingException will be thrown.

You can also retrieve query parameters from method's parameter, simply specify it into @Action annotation. Note that you can't specify a primitive type (int, double, boolean,...) as action's parameter. Parameters into annotation must be declared in the same order as in method. Here is an example:

/**
 * Here, ${0} is the path parameter (at position 0) and order_by
 * and mode are query parameters.
 */
@Action(path = "/${int}", params = "${0}, order_by, mode")
protected void show(final Long id, final String order, final Boolean mode)
{
   ...
}

I suggest you to take a look at the javadoc' of the class Parameter for more informations.

Rendering views

Thanks to Doff, rendering views (JSP) made easy. Doff provides a way to organize your views between controllers. The method for rendering views is called « renderView ». There is three versions of this method.

The first one takes three parameters, the name of the view, the directory where view is located and the extension of the view (including the dot).

The second version takes only the view's name (without the extension and the base directory) as parameter. Views are assumed to be stored in /WEB-INF/views directory (and directories below), and to be stored in files with a .jsp extension.

The third version takes no parameter. View's name is determined from the current action method name preceded by the controller's class simple name. All uppercase letters are preceded by an underscore (_) and converted to lowercase. The simple class name of the controller represents the subdirectory, the name of the action method, the file. Like the second version, views (and subdirectories) are assumed to be stored in /WEB-INF/views directory, and to be stored in files with a .jsp extension.

Here is an example of three versions. We assumes that we are in the action index of the controller UsersGroups. In this case, three lines below does the same thing.

renderView("index", "/WEB-INF/views/users_groups", ".jsp");
renderView("users_groups/index");
renderView();

I suggest you to take a look to the javadoc' of the Service class for more informations.

Redirections

We will now see how to manage HTTP redirections. There is many versions of « redirect » method. We will see all of those versions on the following examples.

We assumes that server is running on localhost, on the port 8080. The context path is webapp and the servlet path is /do.

/**
 * Redirect to http://localhost:8080/webapp/do/users/42
 *
 * Here, the redirection is relative to the context and to the servlet path.
 */
redirect("/users/42");
/**
 * Redirect to http://localhost:8080/webapp/images/logo.png
 *
 * The boolean parameter indicates that we don't want to include the servlet
 * path (/do). The redirection is context relative.
 */
redirect("/images/logo.png", false);
/**
 * Redirect to http://www.google.com
 *
 * If specified URL starts with a protocol, the redirection will be done
 * directly to this URL. If you add a boolean parameter like the previous
 * example, it will be simply ignored.
 */
redirect("http://www.google.com");
/**
 * Redirect to http://www.google.com
 *
 * Here, we use the URL object. 
 */
redirect(new URL("http://www.google.com"));
/**
 * Redirect to the action "index" (method called "index") of the controller
 * Overview.
 *
 * This method works only if the action uses GET HTTP method.
 */
redirect(Overview.class, "index");
/**
 * Redirect to the action "process" (method called "process") of the controller
 * Downlaods but adds path parameters "0.9" and "full_zip" and query string
 * parameter "compress=true".
 *
 * The path of the process action is: /${string}/${string}
 *
 * Third parameter ("compress=true") is considered as query string parameter
 * because process action takes just two path parameters.
 *
 * URL will be: http://localhost:8080/webapp/do/downloads/0.9/full_zip?compress=true
 */
redirect(Downloads.class, "process", "0.9", "full_zip", "compress=true");
/**
 * Redirect to public document ".../javascript/prototype.js"
 */
redirectToPublic("/prototype.js");

I suggest you to take a look to the javadoc' of the Service class for more informations.

Handling file upload

With Doff, handling file upload is very simple. Just call getUploadedFile() method in order to retrieve the unique uploaded file or getUploadedFile(String) if you want the to specify the field name.

Both of those methods returns a FileItem, I suggest you to take a look to the Commons FileUpload documentation for more information, but its usage is very simple, really.

If you need to retrieve the entire list of uploaded files, invoke getUploadedFiles() method. That's all!

The default size limit of a request is 2 megabytes. If you need to customize the file upload take a look to the javadoc of the getUploadedFile() method.

Here is an example for retrieving file upload from field « picture »:

getUploadedFile("picture");

Doff's taglib

Doff provides some functions for JSPs:

Doff also provides a tag to use POST method with links:

<doff:postLink url="/users/delete" method="delete" confirm="Really?">Click here</doff:postLink>

or if you want to specify controller's action and the method to use:

<doff:postLink controller="Users" action="delete" confirm="Really?">Click here</doff:postLink>

Use doff:url tag in order to obtain action's URL. If you don't specify "controller" attribute, the current controller is used. The default value of "action" attribute is : "index" (same behaviour in doff:postLink tag). The controller's name is determined from its class name. You can change it with @Name annotation.

<doff:url controller="Users" action="show" />

In order to obtain public documents paths (stylesheets, javascript,...), use doff:publicURL tag. Defaut public documents path is: "/public". It can be changed into "web.xml" file.

<doff:publicURL value="/javascript/prototype.js" />

A tag that allows to remove blank lines and whitespaces is also available. "trim" attribute indicates if we want to remove whitespaces (default value: true). "keepLines" attribute indicates if we want to keep end of line delimiters (\n) only in non-blank lines. Its default value is "false".

<doff:strip trim="true" keepLines="false">...</doff:strip>

You can use the prefix doff before each of those functions. Don't forget to add following line before using those functions:

<%@ taglib prefix="doff" uri="http://doff.alweb.org/jsp/taglib" %>

And those lines to the web.xml file.

<taglib>
    <taglib-uri>http://doff.alweb.org/jsp/taglib</taglib-uri>
    <taglib-location>/WEB-INF/tld/doff.tld</taglib-location>
</taglib>

You can find the TLD file here and the TagLib documentation here.

Using a dispatcher

If you want to remove the path info (/do) to your URLs, you can use a dispatcher (or the mod jk from apache). The dispatcher is a servlet providing a way to render static resources from the path info. Files under WEB-INF subdirectory are not rendrered. It uses index.html and index.htm directory index files.

You just need to add following lines to your web.xml file:

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.alweb.doff.Dispatcher</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/public/*</url-pattern>
</servlet-mapping>

Don't forget to modify your previous url-pattern from /do/* to /*:

<servlet-mapping>
    <servlet-name>main</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

Then URL to a static resource can be for example:

http://localhost:8080/webapp/public/images/logo.png

and URL for a dynamic resource can be:

http://localhost:8080/webapp/home

Note that the /public/* url-pattern can be modified as you want to. It is preferable in a production environment to use the mod jk from apache.

Debugger

Doff provides a debugger (disabled by default) in order to display current mapped controllers and filters. There is two methods to enable it.

The first method (recommended) is to add a context param called org.alweb.doff.debug with value true into the web.xml file:

<context-param>
    <param-name>org.alweb.doff.debug</param-name>
    <param-value>true</param-value>
</context-param>

<servlet>
    ...

The second method consists in activating debugger by the code. You only need to add the following line:

Engine.getInstance().enableDebugger();

Then, you can access to the debugger through your browser, from URL .../do/doff/debugger. Here is an example of debugger in action. Doff's can be reloaded into debugger only if request is local.

Updating doff

Doff provides an update system of the jar. Try this command:

$ java -jar doff-1.5.1.jar
Doff trunk - Take control of your web applications

-g --generate <WebApp>                   : generates a web application skeleton
-u --update                              : update doff's jar
-l --latest                              : print latest doff version
-v --version                             : print doff's version
-d --download <latest|version> <type>... : download a doff's file

  Available types are: documentation, full_tarball, full_zip, jar, source

More informations at: http://doff.alweb.org

Then, you can obtain you current doff's version:

$ java -jar doff-1.4.1.jar -v
1.4.1

You can also obtain the latest doff's version:

$ java -jar doff-1.4.1.jar -l
1.5.1

Then, to update you doff's jar, type this:

$ java -jar doff-1.4.1.jar -u
Do you want to download doff-1.5.1.jar? [y|n] y
Downloading doff-1.5.1.jar ... finished
Saved to /u/al/doff-1.5.1.jar

You can download previous doff's releases and other files:

$ java -jar doff-1.5.1.jar -d 1.0 source
Do you want to download doff-src-1.0.zip? [y|n] y
Downloading doff-src-1.0.zip ... finished
Saved to /u/al/doff-src-1.0.zip