Getting started
- Creating the webapp
- Your first controller
- About actions
- Some useful methods
- And what about filters
- Obtaining parameters
- Rendering views
- Redirections
- Handling file upload
- Doff's taglib
- Using a dispatcher
- Debugger
- Updating doff
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:
-
${string}: Matches a string buts stops when a slash (/) is found. -
${int}: Matches an int or a long. -
${all}: Matches a string and even slashes.
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.
-
getRequest(): returns the current HTTP servlet request. -
getResponse(): returns the current HTTP servlet response. -
addCookie(String, String, [int]): creates a cookie. -
getCookie(String): returns a cookie value. -
getHeader(Header|String): returns an HTTP header value. -
getHeaders(): returns all HTTP headers. -
getURL(): returns the current URL (many versions of it). -
getMethod(): returns the current HTTP method. -
getSession(): returns the current HTTP session. -
setRequestAttribute(String, Object): set an attribute into the current request. -
getWriter(): returns a PrintWriter that can send character text to the client. -
println(): writes on PrintWriter. -
getOutputStream(): returns the servlet output stream. -
getAction(): returns the action mapped by current URL. -
flash(String, Object): provides a way to display a message only one time after a redirection. - ...
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:
- autoLink(text): Turns all URLs and email addresses into specified text into clickable links.
- autoLinkMailAddress(text): Turns all email addresses into specified text into clickable links.
- autoLinkURL(text): Turns all URLs into specified text into clickable links.
-
call(object, methodName): Invokes a method on specified object even if method's name doesn't starts with
get. - escapeJavascript(text): Escape specified javascript string.
- formatText(text): Returns text transformed into HTML using simple formatting rules.
-
join(collection, delimiter): Joins a collection of objects separated by a delimiter. It uses
toStringmethod each object. Null objects are ignored. -
joinArray(array, delimiter): Joins an array of objects separated by a delimiter. It uses
toStringmethod each object. Null objects are ignored. - now(): Returns the current date.
- truncate(string, maxLength, replacement): Truncates specified string if it is greater than specified length and adds (if truncated) the replacement.
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

