Actions are controller classes in the web layer that can be directly invoked by a browser request. It is a legacy term coined by the struts framework, and broadly adopted by several other java web frameworks.
Any class that implements the Action interface is considered an Action, but it is strongly recommended to make it also extend AbstractAction in order to take advantage of some convenience methods such as redirect(), forward(), etc. Actions are loaded automatically from the configured classpath packages.
Actions are identified by either their fully qualified names or by a short name that is the name of the class with the "Action" suffix removed, if any: Blog would be the short name of com.acme.action.BlogAction. This short name can be used to indicate the target action of a form, a URL, or a redirect Resolution.
Any public Action method that receives no arguments and returns a Resolution instance is automatically considered an Event, which corresponds to a URL that can be invoked. Event execution will be skipped if any validation or conversion errors occur or any Interceptor decides to interrupt the execution flow.
Actions may use the @Event annotation to define a default event that will be used when no event has been specified. If there is only one event it will automatically be set as default.
Actions and Events will be mapped to the lowercase version of the action short name, using dashes to separate words. This mapping can be modified using JAX-RS annotations to specify a different name or additional parameters embedded in the URL:
Events may also specify the allowed http methods used to invoke it. By default, an event with no specified method is mapped to serve GET and POST requests.
// by default this action would have been mapped as /blog-entries/
@Path("/entries")
public class BlogEntriesAction extends AbstractAction {
// /entries/foo (GET/POST)
public Resolution foo() { ... }
// /entries/foo-bar (DELETE)
@DELETE @Path("foo-bar")
public Resolution bar() { ... }
// receive one "id" parameter
@Path("baz/{id}")
public Resolution baz() { ... }
// receive three parameters, the last one is optional
@Path("create/{entry.year}-{entry.month}-{entry.day?}")
public Resolution create() { ... }
}
Events may receive parameters, in which case the parameter name should be specified unless embedded in the URI. For example:
public class BlogEntriesAction extends AbstractAction {
// name is required
@Path("baz")
public Resolution baz(@QueryParam("id") int id) { ... }
// name is not required, since it is embedded in the URL
@Path("bar/{foo}")
public Resolution bar(String foo) { ... }
// You can mix embedded and non-embedded parameters, in which case
// embedded are expected to be resolved left-to-right
@Path("bazbar/{foo}")
public Resolution bazbar(String foo, @QueryParam("bar") int bar) { ... }
}
Since browsers only support GET and POST, forms will include a internal hidden field to override the http method for any HEAD, DELETE or PUT event (this will work out-of-the-box). Any link (a href) pointing to a non-GET event will generate a "method" attribute, but you will need to intercept the onclick event yourself to make the actual event get called.
Any conversion error of embedded parameters (e.g. alphanumeric text for integer parameters) will be transformed to a http 400 error response (malformed request).
Frequently you will need to generate a URL pointing to a target action and event. UrlBuilder includes a simple API to generate URLs:
// return blog/save?foo=bar
UrlBuilder url = request.createUrl(BlogAction.class, "save").add("foo", "bar");
url.getURL();
// transform the current request from http:// into https://: and add a new parameter:
request.toUrlBuilder().setScheme("https").add("redirected", "true");
// parameters are transformed using the configured Converter
UrlBuilder url = new UrlBuilder(SomeAction.class, "someEvent");
url.add("enumValue", CustomerTypeEnum.VIP_TYPE).add("date", new Date());
// urls are by default relative, but they can be forced to include hostname, scheme and port
return new UrlBuilder("/").setRelative(false).getURL();
The following is the execution flow diagram of a Loom request:
The execution flow may be interrupted at any time by configured interceptors in this order:
If the current request has Conversion or Validation errors the event method invocation will be skipped.
Interceptors are classes that implement the corresponding listener interface and may decide to stop the workflow by returning a non-null Resolution or let it continue. Interceptors can implement more than one listener interface, and are associated to Event instances by AnnotationProcessors. For concrete examples see the Interceptor class hierarchy.