ActionServlet Specification
1. Definitions of terms
1.1 Components
are objects which action methods are invoked by HTTP requests.
Each component is referenced by ActionConfig, where
it has specified name, Java class and persistence
and (optionally) instantiator.
Name refers component in <property>,
<input-variable> and
<output-variable> elements. Class determines
which class will be used to instantiate the component. Persistence determines durability
of a component object and may be one of three types:
- "application" persistence - the component object exists in only in one
copy per servlet. This is useful for implementing "global" methods that manipulate
with common application data.
- "session" persistence - each HTTP session owns a single copy of the
component object. This is useful for implementing "shopping carts" etc.
- "request" persistence - for each HTTP request is created a new component object.
- "static" persistence - may be used if only static action methods of the
component class are invoked (component is never instantiated).
If the component wants to be notified when it
is no longer used by ActionServlet, it should implement
Destroyed interface.
1.1.1 Instantiators
Component classes can be of any type (JavaBeans, EJBs, Corba objects etc.) and are created by
instantiators (classes that implement org.actionframework.instantiators.Instantiator
interface). Instantiators are referred by instantiator attribute of
<component> element.
If no instantiator is specified, the
default instantiator
is used.
1.2 Action methods
implement the behaviour of a component. Each action method must be declared public.
To parameters of action method are passed values of HTTP parameters automatically
converted to Java types by type handlers}. Return values can be mapped
to template names by <on-return>
element and exceptions handled by <on-exception>s.
The action method to be invoked is chosen after the name of parameter 'action' and,
optionally, 'form' (if omitted, it has a default empty value: ""). For example,
"login" action would invoke someComponent.login(...) method. Alternatively,
any URL can be mapped to an action (see regex actions).
Action selection can be also based on the request type, which means that some action my be
limited to HTTP GET or POST requests only or require HTTPS. See <action>
element for detailed information.
1.3 Templates
You should be familiar with WebMacro or
Velocity templates
already. The new thing is that you can pass HTTP parameter value(s) from template
directly to the component's action method.
Example: the following HTML form defines a parameter named userName
and password (and of course, the mandatory 'action'):
<FORM METHOD="POST">
User name: <INPUT TYPE="Text" NAME="userName">
Password: <INPUT TYPE="Password" NAME="password">
<INPUT TYPE="Submit" NAME="action" VALUE="Login">
</FORM>
Values of these parameters can be easily passed to the action method:
public int login(String userName, String password)
Note: A similar effect can be achieved by GET method - using a link like:
<A HREF="$SERVLET?action=Login&userName=John+password=18x79Z">login as John</A>
1.4 Type handlers
are used for conversions between different types in the case when the target type is not assignable from
the source type. Assignable target types are assigned by implicit conversion. For example, java.lang.Integer
is assignable to java.lang.Number (because Number is a supertype of Integer), but to assign
java.lang.Number to java.lang.Integer a type handler is needed.
Type handlers provide a convenient way to link various components together (see also
<invoke> element), where the conversions of parameters types is done by
separate (i.e. more independent and maintainable) code:

With servlets, most commonly are used type handlers for conversion from java.lang.String (supplied in HTTP
request from <FORM> parameters or in URL) to other types - int, float, double etc. ActionServlet has
already defined type handlers from java.lang.String to all primitive types. Also, a "dummy handler" for
converting standard wrapper classes to primitive types (like Integer to int) and vice versa is set.
Arrays are also passed to action methods automatically. If an array is needed to be converted to a
non-array type with the same component type (for example, String[] to String) and a type handler for this
operation is not defined, default "dummy array handler" - which just returns the first element of the array - is used
(this is useful especially when values of HTTP parameters are retrieved). In case of array-to-array conversion (int[] to
Integer[], for example *), if no type handler for array conversion is found, elements are converted one by one
(by default dummy handler - for *).
To support file uploading, virtual type handlers from java.lang.String to
java.io.File and to java.io.InputStream are also defined. This means that you can
pass the content of uploaded file to an action method as a temporarily stored File or directly as
an InputStream - depending on your needs (see examples/FileUploads).
Every type handler must implement one of these interfaces:
SimpleTypeHandler
and CompositeTypeHandler
or ContentTypeHandler.
Each type handler must have a default or a public no-argument constructor or a public constructor with a single
parameter of type org.actionframework.ActionServlet (or its subclass).
For what types the type handler performs conversions is specified by <type-handler>
elements in ActionConfig.
1.4.1 Type handlers and actions
Type handlers are used automatically when action method is to be called.
[Note: You may need to read the rest of this specification first.]
The following description refers to this ActionConfig example:
<type-handlers>
<type-handler target-type="java.util.Date" class="DateHandler"/>
</type-handlers>
<component name="DateComponent" class="DateComponent" persistence="application">
<action name="Submit" method="submit(java.util.Date date)">
<input-variable name="date" value="day,month,year"/>
<on-return value="void" show-template="SubmittedDate.wm"/>
</action>
</component>
When a HTTP request is made by user:
- Action method is selected - according to values of "form" and "action" parameters
(for normal action) or action URL (for regex actions) - see <action>
element for more info.
In the example:
If there is a parameter named "action" and has the value "Submit",
then submit(...) method is selected.
- Values of all parameters named as defined in the action method definition are retrieved - search order: a) Cotext,
b) requested URL (only for regex action), c) HTTP request, d) <input-variable>s
of the action (may also override previously retrieved value).
In the example:
Value(s) of parameter named date is retrieved from
<input-variable> (and overridden if also defined in the HTTP request).
- Parameter values are converted to target types one by one using type handlers. Every type handler is selected,
(as defined in <type-handlers> elements) according to the source-type
of the parameter value (java.lang.String for URL and HTTP parameters) and target-type specified in
action method definition (which corresponds to the parameter type in method signature).
Note: <input-variable>s and <output-variable>s
may also specify a type handler directly.
In the example:
Type handler DateHandler that converts java.lang.String to
java.util.Date is selected.
Note: If the selected type handler is CompositeTypeHandler,
then parameters from the composite definition (day, month, year) are also retrieved and passed to
the handler.
- Converted values are put to Context and passed to the action method.
Exceptions to this schema and other information are in the HTTP request processing section.
2 ActionConfig (XML configuration file)
has a simple XML format and is divided into four main parts:
<properties> (optional),
<templates> (optional),
<type-handlers> (optional) and
<components> (mandatory):
<?xml version="1.0"?>
<!DOCTYPE application SYSTEM
"http://www.actionframework.org/dtd/ActionServlet_0_94.dtd">
<application>
<properties>
...
</properties>
<templates>
...
</templates>
<type-handlers>
...
</type-handlers>
<components>
...
</components>
</application>
ActionConfig filename must be passed to ActionServlet via a servlet init parameter named
"ActionConfig". For Servlet 2.2+ containers it is done inside the servlet
tag in web.xml file.
Optional servlet init parameter named "ActionEngine" may specify template engine.
The value of this parameter may be:
- WebMacro - for use with WebMacro 1.0+ (default),
- Velocity - for use with Velocity 1.1+,
- fully.qualified.class.name - custom engine wrapper class name
[TODO: Pluggable engine API will be public in ActionServlet 0.96].
Example: (piece of web.xml):
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>org.actionframework.ActionServlet</servlet-class>
<init-param>
<param-name>ActionConfig</param-name>
<param-value>LoginServlet.xml</param-value>
</init-param>
<init-param>
<param-name>ActionEngine</param-name>
<param-value>Velocity</param-value>
</init-param>
</servlet>
For other environments constult your servlet runtime documentation.
Note: Velocity.properties file can passed to ActionServlet through init parameter
named properties as usual (path can be relative to the servlet context).
Example:
<init-param>
<param-name>properties</param-name>
<param-value>WEB-INF/classes/Velocity.properties</param-value>
</init-param>
2.1 The root <application> element may have optional repository
attribute, which specifies a location where servlet components and other classes are stored.
By default, this attribute may be used only if you don't subclass ActionServlet, otherwise it is expected that all classes are loaded from CLASSPATH
or relatively to the servlet real path - by the servlet runtime, as usually. Subelements of <application>
are described in the following sections.
Example:
<application repository="c:/servlets/App1/classes">
...
</application>
Note: Custom class loading is possible by overriding getClassLoader() method.
2.2 <properties> element contains one or more
<property> subelements, which have attributes name, value
and optionally component (refers to a <component> name).
Example:
<properties>
<property name="server.url" value="http://some.server.com"/>
<property component="DbAccess" name="max.connections" value="10"/>
</properties>
Alternatively, <property> can contain <input-variable>,
in which case the value for property is retrieved from this subelement. Value of the <property> must
correspond to the name of <input-variable>.
Example:
<properties>
<property name="jndiContext" value="ctx">
<input-variable name="ctx" component="JNDIComponent" value="getContext()"/>
</property>
</properties>
Note 1: Value of each property is retrieved by getProperty(name) and
getProperty(component,name).
Note 2: ActionServlet components should be configured via properties with component attribute
(other properties should be considered global).
Note 3: Usage of JavaBeanInstantiator is highly recommended.
2.2.1 Standard properties
Global property names starting with org.actionframework are reserved.
Currently, the following properties can be used to configure ActionServlet.
- File-uploads related properties (see also examples/FileUploads):
- org.actionframework.ActionServlet.allowUploads - must be set to "true" to allow file uploads,
- org.actionframework.ActionServlet.maxFileSize - optionally sets maximum size of uploaded file
(default: 2MB),
- org.actionframework.ActionServlet.uploadDir - must be set if uploaded files are to be passed
as java.io.File (if not set, java.io.InputStream has to be used).
- Other properties:
- org.actionframework.reloadActionConfig - if set to "true", ActionServet will
reload ActionConfig each time it changes and reinitialize runtime (useful for development).
- org.actionframework.logAllTypeHandlers - if set to "true", invocations of all type handlers
will be logged (by default org.actionframework.* type handlers are excluded from the log).
- org.actionframework.ActionServlet.printStackTrace - if set to "true" (default),
error() method
will print exceptions' stack trace (useful for development).
- org.actionframework.ActionServlet.automaticImageToBooleanConversion - if "true", value of "foo" parameter
from the request would be retreived as "true" from <INPUT TYPE="Image" NAME="foo">
(sent as "foo.x=0&foo.y=0" by browser).
- org.actionframework.ActionServlet.passAllHttpParams - if "true",
all HTTP parameters will be put into Context automatically.
2.3 <templates> element contains one or more
<template> and zero or more <on-exception> subelements.
Example:
<templates>
<template name="First.wm"/>
<template name="Second.wm"/>
<template name="Third.wm"/>
<on-exception class="java.lang.Exception" show-tepmplate="error.wm">
<output-variable name="error" value="Error in template ocurred!"/>
<on-exception>
</templates>
2.3.1 One or more <template> elements
can be nested in <templates> element.
These elements are used to specify:
- which template is shown upon a new session - by is-new-session attribute (if this
attribute is never set to "true", the
newSession() method is
expected to do the job).
- which <output-variable>s will be set when the template
is shown (optional),
- zero and more <invoke> elements - to define actions
to be invoked when the template is displayed.
Each <template> element must have a name attribute specifying the name of
the template. A non new-session template name can also contain a regular expression (Perl5 compatible), which allows to specify
common subelements processed for multiple temples.
Example:
<templates>
<template name="Login.wm"/>
<template name="Main.wm" is-new-session="true">
<output-variable name="isAuthenticated"
component="Authenticator" value="isAuthenticated()"/>
<output-variable name="user"
component="Authenticator" value="getUser()"/>
</template>
<template name=".*">
<output-variable name="someString" value="Yes!"/>
</template>
</templates>
In the example is set $variable isAuthenticated to the value returned by isAuthenticated()
of component Authenticator and $user to the value returned by Authenticator.getUser() -
every time Main.wm template is shown. Variable someString is set to "Yes!" for all templates.
2.3.1.1 Zero or more <include-template> elements
can be nested in <template> element. Each <include-template>
element has attributes:
- name (mandatory) - is an expression that evaluates to a template name,
- if (optional) - specifies a condition which must be true in order to process
this element.
If there is no condition or the condition is true, <template> element
of the specified template name is processed. This is useful when several templates are combined into one page.
Example:
<templates>
<template name="Main.wm" is-new-session="true">
<include-template name="Login.inc.wm"/>
<include-template name="$currentSkin/footer.inc.wm"/>
</template>
<template name="Login.inc.wm"/>
<output-variable .../>
<output-variable .../>
<template>
<template name="default/footer.inc.wm"/>
<template name="nice/footer.inc.wm"/>
</templates>
2.4 <type-handlers> element contains one
or more <type-handler> subelements, that bind type handlers to
appropriate Java types.
Each <type-handler> element has attributes:
- source-type (optional) - contains the name of source type class including primitive
types and arrays (if not used "java.lang.String is the default value),
- target-type - contains the name of target type class including primitive types and arrays,
- class - contains the class name of type handler.
Example:
<type-handlers>
<type-handler target-type="my.types.Email[]"
class="my.handlers.ArrayEmailHandler"/>
<type-handler target-type="java.util.Date"
class="my.handlers.DateHandler"/>
<type-handler source-type="java.io.File"
target-type="java.io.FileInputStream"
class="my.handlers.FileHandler"/>
</type-handlers>
Note: Inheritance of types is taken in account - for example, if you declare type handler for
conversion from java.lang.Number to int, then all subclasses of java.lang.Number
(i.e. java.lang.Byte, java.lang.Double etc.) will be converted automatically
to int by this handler (if this setting is not overridden by another
<type-handler> element).
Example:
<type-handlers>
<type-handler source-type="java.lang.Number"
target-type="int"
class="some.Handler"/>
<-- the following element overrides handler on the previous line -->
<type-handler source-type="java.math.BigInteger"
target-type="int"
class="other.Handler"/>
</type-handlers>
Note 1: Whole tree of type handlers bindings is dumped to log during initialization of ActionServlet,
if logging level is set to DEBUG (see examples).
Note 2: See also <invoke> to see how type handlers are also used.
2.5 <components> element contains:
Example:
<components>
<component .../>
<component .../>
<on-return .../>
<on-exception .../>
</components>
2.5.1 One or more <component> elements
must be specified under <components> element.
Each <component> element has attributes:
- name - name of the component - may be referred in <output-variable>
elements (name is also passed to instantiator).
- class - fully qualified class name of the component (also passed to instantiator).
- persistence - see Section 1.1 for valid values and explanation.
- instantiator (optional) - fully qualified class name of the instantiator
(only instantiators from org.actionframework.instantiators
package need not to be fully qualified).
Example:
<component class="Authenticator" persistence="application">
<action name="Login" method="login(String userName, String password)"/>
<on-return value="void" show-template="SuccessfulLogin.wm"/>
<on-exception class="LoginException" show-template="LoginError.wm"/>
</component>
2.5.2 Zero or more <action> elements, which bind
actions to components' action methods, can be specified inside each <component>
element (before <on-return> elements). Element <action> has attributes:
Example:
<component name="Searcher" ...>
<action form="Search" name="OK" method="search(String str)"/>
<action form="Submit" name="OK" method="newURL(java.net.URL url)"/>
<action name="/download/*/images" method="getImage(String imgName)"/>
...
</component>
<component name="Authenticator" ...>
<action name="getUserCount" method="getUsersByName(String userName).size()"/>
</component>
<component name="MyComponent" ...>
<action name="*unassigned*" method="doDefaultAction()"/>
</component>
This maps http://some.server/yourServlet?form=Search&action=OK&str=Java to
search() method, http://some.server/yourServlet?form=Submit&action=OK&url=http://added.url/whatever to
submit() method, http://some.server/yourServlet/download/1/images?imgName=some.gif
to getImage() method and so on. MyComponent.doDefaultAction() would be called for all unmatched actions.
Element <action> can optionally contain subelements:
- one or more <input-variable>s, which can be used
to assign values to parameters of the action - instead of normal HTTP parameter retrieval - or to override
retrieved values. Input variables are evaluated in the specified order before action invocation.
- one or more <output-variable>s, which put additional
variables to Context of the action. Output variables are evaluated in the specified order after the
action invocation.
Example:
<action name="Login" method="login(String userName, String password)">
<input-variable name="userName" value="defaultUser" if="!userName"/>
<input-variable name="password" value="defaultPassword" if="!password"/>
<output-variable name="user" component="Authenticator" value="getUser()"/>
</action>
Result of the action can be manipulated using <on-return>
and <on-exception> element.
2.6 Zero or more <input-variable> elements
can be specified inside <property> or <action>.
Each <input-variable> has attributes:
- name (mandatory) - determines name of the $variable,
- component (optional) - refers to the component's name from which the
value is taken (the component is instantiated, if it doesn't yet exist),
- value - operator applied to the component (a method call, for example)
or to a $variable or it can be directly boolean, int, double or String value.
- if (optional) - specifies a condition which must be true in order to evaluate
this $variable.
Note 1: If component is specified and value is "this", then $variable will be
assigned the component itself.
Note 2: If component is specified and value is "class", then $variable will be
assigned the component class.
Note 3. In the value attribute may be accessed values of $variables that are in the context.
This element may also have nested <type-handler>
(that has only class attribute in such case), which is used to override "global"
<type-handler>.
Example:
<input-variable name="userName"
value="defaultUserName"
if="!$userName || $userName.length()==0"/>
<input-variable name="source" component="SomeComponent" value="this"/>
<input-variable name="value" component="OtherComponent" value="getObject()">
<type-handler class="my.package.MyHandler"/>
</input-variable>
2.7 Zero or more <output-variable> elements
can be specified inside <template>, <action>,
<on-return> or <on-exception>
elements. It has four attributes:
- name (mandatory) - determines name of the $variable,
- component (optional) - refers to the component's name from which the
value is taken (the component is instantiated, if it doesn't yet exist),
- value - operator applied to the component (a method call, for example)
or to a $variable or it can be directly boolean, int, double or String value.
- if (optional) - specifies a condition which must be true in order to evaluate
this $variable.
Note 1. First are evaluated output variables of <action>, then of
<on-return> and at last of <template>.
Note 2. If component is specified and value is "this", then $variable will be
assigned the component itself.
Note 3. If component is specified and value is "class", then $variable will be
assigned the component class.
Note 4. In the value attribute may be accessed values of $variables that are in the context (see below).
This element may also have nested <type-handler>
(that has only class attribute in such case), which is used to override "global"
<type-handlers>.
Example:
<output-variable name="user" component="Authenticator" value="getUser()"/>
<output-variable name="name" value="$user.Name"/>
<output-variable name="number" value="123" if="$name == "admin""/>
<output-variable name="component" component="Counter" value="setCounter($number)"/>
<output-variable name="component" component="SomeComponent" value="this"/>
<output-variable name="value" component="OtherComponent" value="getObject()">
<type-handler class="my.package.MyHandler"/>
</output-variable>
<output-variable name="newURL" value="$URL/setUser/$name"/>
See also: examples/OutputVars.
2.8 Zero or more <on-return> elements
can be specified:
Each <on-return> has attributes:
- value (mandatory) - which contains:
- name of public static final field of the nesting component class or
- fully qualified name of public static final field
(in these two cases the field's value will be compared to the return value of
invoked action method) or
- a boolean (true/false - case insensitive) or
- an integer value or
- a special name "void" - reserved for mapping of methods with void return type - or
- a special name "*" - for any returned value that doesn't match any other value-s,
- assign-to (optional) - contains the name of $variable, which will be assigned the returned value,
- and one of optional attributes:
- show-template - contains the name of template to be shown,
- show-url - contains either:
- variable name from assign-to attribute or
- variable name from nested <output-variable>,
which value will be used as URL for redirect - see also
getTemplate(),
- show-value-of - contains the name of $variable, which value will be written directly
to HTTP response, and it can match either:
- variable name from assign-to attribute or
- variable name from nested <output-variable>.
In both cases a ContentTypeHandler
will be used to convert variable's value to java.io.InputStream - so there must be a
corresponding binding inside <type-handlers> element (see
examples/FileUploads example for more info).
Zero or more <invoke> elements, which allow invocation of a subaction,
and zero or more <output-variable>s can be nested inside <on-return> - evaluation
and invocation takes place in the specified order.
Example:
public class MyComponent {
public static final int OK = 0;
int firstMethod() {
return OK;
}
boolean secondMethod() {
return true;
}
int thirdMethod(int number) {
return number * 2;
}
void fourthMethod() {}
}
<component class="MyComponent" ...>
<action name="action1" method="firstMethod()"/>
<action name="action2" method="secondMethod()">
<on-return value="true" show-template="Template1.wm">
<invoke method="firstMethod()"/>
<output-variable name="number" value="123"/>
<invoke method="fourthMethod()"/>
</on-return>
</action>
<action name="action3" method="thirdMethod()">
<on-return value="*" assign-to="result" show-template="Template2.wm"/>
</action>
<action name="action4" method="fourthMethod()"/>
<on-return value="OK" show-template="Template3.wm"/>
<on-return value="void" show-url="myURL">
<output-variable name="myURL" value="http://www.actionframework.org"/>
</on-return>
</component>
- If "action1" is invoked, "Template3.wm" will be displayed.
- If "action2" is invoked, firstMethod() will be called, then number
$variable evaluated, then fourthMethod() called and "Template1.wm" displayed.
- If "action3" is invoked, "Template2.wm" will be displayed (and return value assigned to $result).
- If "action4" is invoked, the browser will redirect to http://www.actionframework.org.
2.9 Zero or more <on-exception> elements
can be specified:
and also:
Each <on-exception> has attributes:
- class (mandatory) - fully qualified class name of the exception,
- assign-to (optional) - contains the name of $variable, which will be assigned the thrown exception,
- rethrow-if (optional) - contains an expression, which is is evaluated at the
end of <on-exception> element processing (i.e. after its
<invoke>s,
<output-variable>s etc.) - if the result is true, then the
exception is propagated to parent <on-exception>s or
onException() method,
- and one of optional attributes:
- show-template - contains the name of template to be shown,
- show-url - contains either:
- variable name from assign-to attribute or
- variable name from nested <output-variable>,
which value will be used as URL for redirect - see also
getTemplate(),
- show-value-of - contains the name of $variable, which value will be written directly
to HTTP response, and it can match either:
- variable name from assign-to attribute or
- variable name from nested <output-variable>.
In both cases a ContentTypeHandler
will be used to convert variable's value to java.io.InputStream - so there must be a
corresponding binding inside <type-handlers> element (see
examples/FileUploads example for more info).
Zero or more <invoke> elements, which allow invocation of a subaction,
and zero or more <output-variable>s can be nested inside <on-return> - evaluation
and invocation takes place in the specified order.
The position of <on-exception>s has a similar meaning as for try-catch blocks:
<on-exception>s inside <component>s (if any) handle uncaught exceptions
from its <action>s and <on-exception>s in <components>
(if any) handle uncaught exceptions from all <component>s and all <action>s.
Similarly, uncaught exceptions from <template> are propagated to
<on-exception>s of <templates>.
Example:
public class MyComponent {
void myMethod(int i) throws Exception {
if (i < 0) throw new ArithmeticException();
else if (i > 0) throw new IllegalArgumentException();
else throw new NumberFormatException();
}
}
<components>
<component class="MyComponent" ...>
<action name="myAction" method="myMethod(int i)">
<on-exception class="java.lang.ArithmeticException"
show-template="Template1.wm"/>
<on-exception class="java.lang.NumberFormatException"
show-template="Template2.wm"/>
</action>
<on-exception class="java.lang.Exception" show-template="Template3.wm"/>
</component>
</components>
- If "action" is invoked with i=-1, "Template1.wm" will be displayed.
- If "action" is invoked with i=0, "Template2.wm" will be displayed.
- If "action" is invoked with i=1, "Template3.wm" will be displayed.
2.10 Zero or more <invoke> elements
can be nested inside <template>, <on-return> and
<on-exception>.
This element is used to define methods executed when template is displayed or
when <on-return> and <on-exception> are processed.
Element <invoke> can contain the same subelements as
<action>.
Attributes of <invoke> are:
- method - has the same meaning as for <action>,
- component - refers to the component's name which the method
belongs to. This attribute is mandatory in <template>, inside
<action> may be omitted - in such case the component reference is taken from
enclosing <component> element.
- if (optional) - contains a condition definition that must be true in order to
process this element - this condition is evaluated before method parameters are retrieved.
Example:
<application>
<templates>
<template name="welcome.wm" is-new-session="true">
<output-variable name="welcomeString" value="Welcome!"/>
<invoke component="counter" method="incNumberOfAccesses()"/>
</template>
</templates>
<components>
<component name="counter" class="Counter" persistence="application"/>
<component name="sender" class="Sender" persistence="session">
<action name="getData" method="getData()">
<on-return value="*" assign-to="result" show-template="data.wm">
<invoke component="receiver" method="receiveData(int result)"
if="$result!=0">
<input-variable name="result" value="$result"/>
<invoke/>
</on-return>
</action>
</component>
<component name="receiver" class="Receiver" persistence="session"/>
</components>
</application>
Element <invoke> allows sending data between components and is
useful when you need to pass data of one action to more components.
Type handlers automatically convert parameters values to
target types.
2.11 Zero or more <pre-invoke> elements
can be nested inside <components>, <component> and
<action> - as their first element(s). These elements are invoked when <action> is known, but before
it is processed - and therefore may be used:
- to check for conditions that must be fulfilled in order to invoke the action and/or
- to perform some kind of default action.
Example:
<components>
<pre-invoke component="Logger" method="logNoHttps()"
if="$URL.getProtocol() != "https"">
<on-return value="*" show-template="YouMustUseHttps.wm"/>
</pre-invoke>
<component name="Authenticator" class="Authenticator" persistence="session">
<pre-invoke method="isLoggedIn()">
<on-return value="true"/>
<invoke method="logoff()"/>
</on-return>
</pre-invoke>
<action name="/login" method="login(String userName, String password)"/>
</component>
<component name="Logger" class="Logger" persistence="application"/>
</components>
First are processed <pre-invoke> elements of <components>,
then of <component> and at last of <action>.
Element <pre-invoke> can contain the same subelements as <invoke> plus:
- group (optional) - specifies comma separated list of groups, which correspond to group(s) of <action>s.
If any group of <action> matches some <pre-invoke>'s group, then such <pre-invoke> is processed
before this <action>. If no group is defined for <pre-invoke>, it applies to all
<action>s.
Example:
<components>
<pre-invoke component="Authenticator" method="isAdmin()" group="checkit,grp2">
<on-return value="true" show-template="NotAdminError.wm"/>
</pre-invoke>
<component name="Secure1" class="Secure1" persistence="session">
<action name="/secureAction1" method="secureMethod1()" group="checkIt"/>
</component/>
<component name="Secure2" class="Secure2" persistence="session">
<action name="/secureAction2" method="secureMethod2()" group="grp2"/>
</component/>
</components>
2.12 Zero or more <post-invoke> elements
can be nested inside <components>, <component> and
<action> - as their last element(s). These elements are invoked after <action>
is processed - and therefore may be used to perform some kind of default post-action.
Example:
<components>
<component name="Authenticator" class="Authenticator" persistence="session">
<action name="/login" method="login(String userName, String password)"/>
</component>
<component name="Logger" class="Logger" persistence="application"/>
<post-invoke component="Logger" method="logAction(String url, String action)">
<input-variable name="url" value="$URL"/>
<input-variable name="action" value="$ACTION"/>
</post-invoke>
</components>
First are processed <post-invoke> elements of <action>,
then of <component> and at last of <components>.
Element <post-invoke> can contain the same subelements as <invoke> plus:
- group (optional) - specifies comma separated list of groups, which correspond to group(s) of <action>s.
If any group of <action> matches some <post-invoke>'s group, then such <post-invoke> is processed
before this <action>. If no group is defined for <post-invoke>, it applies to all
<action>s.
Example:
<components>
<component name="C1" class="C1" persistence="session">
<action name="/someAction1" method="someMethod1()" group="grp1"/>
</component/>
<component name="C2" class="C2" persistence="session">
<action name="/someAction2" method="someMethod2()" group="grp1,grp2"/>
</component/>
<component name="Logger" class="Logger" persistence="application"/>
<post-invoke component="Logger" method="logAction(String action)" group="grp2">
<input-variable name="action" value="$ACTION"/>
</post-invoke>
</components>
2.13 Zero or more <validate> elements
can be nested inside <action>, <invoke>,
<pre-invoke> and <post-invoke> - after
their <input-variable>s.
These elements are invoked after parameter conversion is done and may be used to validate values of parameters.
Element <validate> has no nested elements and has attributes:
- method - has the same meaning as for <action>,
- component - refers to the component's name which the method
belongs to. This attribute is mandatory in <template>, inside
<action> may be omitted - in such case the component reference is taken from
enclosing <component> element.
Example:
<action name="/submitEmail" method="login(String email)">
<validate component="validator" method="validate(String email)"/>
<on-exception class="my.package.BadEmailException" show-template="BadEmail.wm"/>
</action>
Note that any method (that throws some exception) of any component may be used for validation!
2.14 All-in-one example
An example of ActionConfig may look like this (not all combinations and elements are used):
<?xml version="1.0"?>
<!DOCTYPE application SYSTEM
"http://www.actionframework.org/dtd/ActionServlet_0_94.dtd">
<application>
<properties>
<property name="MaxUsers" value="500"/>
<property component="Authenticator"
name="PasswordRetries" value="3"/>
</properties>
<templates>
<template name="Login.wm" is-new-session="true">
<invoke component="Main" method="incNumberOfAccesses()"/>
</template>
<template name="Main.wm">
<output-variable name="isAuthenticated"
component="Authenticator"
value="isAuthenticated()"/>
</template>
</templates>
<type-handlers>
<type-handler target-type="shop.types.Email"
class="shop.handlers.EmailHandler"/>
</type-handlers>
<components>
<component name="Main" class="shop.Main" persistence="application">
<action name="showMain" method="showMain()"/>
<action name="showGoods" method="showGoods()">
<output-variable name="goods" component="Main" value="getGoods()"/>
</action>
</component>
<component name= "Authenticator"
class="shop.Authenticator"
instantiator="JavaBeansInstantiator"
persistence="session">
<action name="Login" method="login(String name, String passwd)">
<input-variable name="name" value="defaultUser" if="!user"/>
<input-variable name="passwd" value="defaultPasswd" if="!passwd"/>
<on-return value="void" show-template="Main.wm"/>
<on-exception class="LoginException" show-template="error.wm">
<output-variable name="retries"
component="Authenticator" value="getRetries()"/>
</on-exception>
</action>
</component>
</components>
</application>
It may be useful to divide ActionConfig into several parts, instead of one
big XML file. You can use standard external entities to achieve better
readibility:
Example:
Main ActionConfig file:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE application SYSTEM
"http://www.actionframework.org/dtd/ActionServlet_0_94.dtd" [
<!ENTITY my_components SYSTEM "my_components.xml">
]>
<application>
<templates>
<template name="main.wm" is-new-session="true">
</templates>
<components>
&my_components;
</components>
</application>
File my_components.xml then may contain only the XML header and <component> elements:
<?xml version="1.0" encoding="ISO-8859-1"?>
<component name="MyComponent1" class="MyClass1" persistence="session">
<action name="doIt" method="doIt()"/>
<component>
<component name="MyComponent2" class="MyClass2" persistence="static">
<component name="MyComponent3" class="MyClass3" persistence="application">
3. HTTP request processing
An example of typical lifecycle looks like this:

| I. | User clicks "Login" button -> HTTP request with parameters "action",
"userName" and "password" is generated and passed to handle() method of
the servlet. |
| II. | According to <action> mappings in ActionConfig, "login" method
is chosen and invoked - "userName" and "password" parameters value are passed to the method. |
| III. | Return value or thrown exception is mapped to template name by
<on-return> or <on-exception> elements in ActionConfig. For example,
if LoginException is thrown by login() method, then "Login.wm" template name is chosen. |
| IV. | HTTP response is generated from a template and sent to the browser.
|
Detailed description of HTTP request processing follows:
- If org.actionframework.ActionServlet.passAllHttpParams property is set to true, all incoming
HTTP parameters are put into Context.
- Six $variables are put into Context:
- $AC (of type org.actionframework.ActionServlet), which refers the servlet instance,
- $SERVLET (of type java.lang.String), which contains the servlet path in URL,
- $URL (of type java.net.URL), which represents the current URL (without the action and query part),
- $REQUEST (of type javax.servlet.http.HttpServletRequest) and
- $RESPONSE (of type javax.servlet.http.HttpServletResponse).
- $REQUEST_TYPE (of type java.lang.String), which contains the request type (see <action>),
- The request is passed to
handle() method.
ActionServlet requires that 'action' (and optional 'form') HTTP parameters
are specified in the request - or regex action is defined - so the user action of this request
can be determined (as described in 2.5.2). In addition, one more $variable is put into Context:
- $ACTION_NAME (of type java.lang.String), which contains the <action> name.
- <pre-invoke> elements (if any) are processed - in the similar manner like <action> - starting from step 5.
- The component, which method is bound to the <action> (resp. <*invoke>/<validate>), is created - by means of its instantiator (with respect
to the component persistance), if it does not yet exist.
- Values of action method parameters are retrieved - search order: a) Context, b) URL - for regex actions only, c) HTTP request, d) <input-variable>s of the action (processed always, if present!).
beforeConversion() method is called.
- Values of method parameters are converted to the values of Java types by type handlers and put to Context.
- <validate> elements (if any) are processed - like <action> - starting from step 5.
- If overridden,
beforeInvoke() method is called.
- Appropriate action method is invoked by
invokeMethod().
- If overridden,
afterInvoke() method is called (may alter return value from the action method).
- All <output-variable>s of the <action> are evaluated.
- If no exception is thrown, then the return value is compared to:
- <on-return>s of <action> (resp. <*invoke>/<validate>),
- if none matches, then to <on-return>s of <component> (for <action> only),
- if still none matches, then to <on-return>s of all <components> (for <action> only),
- if still none matches, then
onReturn()
method is called and the next step is skipped.
- Otherwise all <output-variable>s of the matched <on-return> element are evaluated.
The resulting action depends on which of attributes was used in the <on-return> element:
- for show-template - the specified template is loaded, rendered and written to HTTP response,
- for show-url - the browser will be redirected to given URL,
- for show-value-of - the value of given $variable will be written to HTTP response,
- If none of these attributes is used, the result will be taken from <invoke>
subelement(s) - or error will be reported.
If <on-return> contains <invoke> elements, they are executed one by one -
in the similar manner like <action> - starting from step 5.
- <post-invoke> elements are processed (skipped for <*invoke>s and <validate>s) - in the similar manner like <action> - starting from step 5.
requestCleanup() method is called.
Exceptions to this schema happen:
- If
beforeInvoke() is called and returns a non-null value - then steps 11 and 12 are skipped.
- If 'action' parameter is not defined in the HTTP request or is not defined for
specified 'form' and it is not a regex action, then:
- "*unassigned*" <action> is invoked - or - if not defined:
unassignedAction() method is called.
- If an exception is thrown by action method, beforeInvoke(), afterInvoke() or exception occurrs while evaluating
<input-variable> or <output-variable> or their 'if' condition, then
<on-exception> elements are processed - the closest first, i.e.:
- first are tried <on-exception>s of <*invoke>/<action>, where the exception ocurred,
- then <on-exception>s of parent <*invoke>/<action> element,
- then <on-exception>s of action's <component> and
- last <on-exception>s of all <components>.
If an exception is thrown in <template>, it is processed by its <on-exception>s or
(because propagation also works) by <on-exception>s of all <templates>.
If there are no <on-exception>s at all or the exception is still not handled, then:
If <on-exception> contains <invoke> element, it is executed just
like <action> - starting from step 5.
Note 1: ActionServlet notifies components when they are no longer used - see Destroyed interface.
Note 2: error.wm template is used to display errors by default. Its presence is recommended for every application.
You can change this behaviour by overriding error() method.
4. Development using ActionServlet (roles)
The development process with ActionServlet is divided into several separate tasks, which
can be performed by different roles:
- Role: Designer
- Designs templates - they provide the look and feel of the web application.
- Role: Developer
- Programs components - they encapsulate the business logic.
- Programs type handlers - they enable custom parameter type conversions.
- Subclasses ActionServlet to modify its default behaviour.
- Role: Assembler
- Creates ActionConfig, which binds actions to components methods, maps return values
to templates, Java types to type handlers, assigns template variables, configures
properties etc.
- Packages the web application.
- Role: Deployer
- Deploys the web application - depends on the servlet runtime (usually in a .war file)
Benefits:
- Designer needs not to know Java and complicated application logic, but only the simple
WebMacro/Velocity scripting language that renders output data.
- Developer does not care about the application look and feel (HTML+JavaScript,WML,...).
- Components can be reusable - assembler can build applications from existing components.
|