The document discusses the Struts 2 framework architecture. It explains that the ActionContext stores the ValueStack which contains request data. The ValueStack holds application domain data for an action invocation and serves as the default root object for resolving OGNL expressions. Various tags like Iterator, If, Url are used to work with and retrieve data from the ValueStack. Results like DispatcherResult handles view resolution by forwarding to JSPs. The validation framework uses validators, validation metadata and domain data to validate action properties.
2. ActionContext and OGNL
The ActionContext stores ValueStack which stores request data.
The ActionContext contains all of the data available to the
framework’s processing of the request, including things ranging
from application data to session- or application-scoped maps.
All application specific data are held in the ValueStack, one of
the objects in the ActionContext.
All OGNL expressions must resolve against one of the objects
contained in the ActionContext.
By default, the ValueStack will be the one chosen for OGNL
resolution, but can specifically name one of the others, such as
the session map.
The ActionContext provides a notion of simple container for all
the important data and resources surrounding the execution of
an action.
The ValueStack holds your application’s domain-specific data for
a given action invocation.
The OGNL expression requires a root object contained in the
ActionContext, against which resolution of references begin.
3. Objects and Maps in ActionContext
parameters: Map of request parameters for the
request.
request: Map of request-scoped attributes.
session: Map of session-scoped attributes.
application: Map of application-scoped attributes.
attr: Returns first occurrence of attribute occurring in
page, request, session, or application scope, in such
sequence.
ValueStack: Contains all the application-domain–
specific data for the request.
4. ActionContext and OGNL
An OGNL expression must choose one of the objects
in the ActionContext to use as its root object.
By default, the ValueStack serves as the root object for
resolving all OGNL expressions.
OGNL expression can be written in following syntax:
#session['user']
The # operator tells OGNL to use the named object,
located in its context, as the initial object for resolving
the rest of the expression.
5. The ValueStack: a virtual object
When Struts 2 receives a request, it immediately creates an
ActionContext, a ValueStack, and an action object.
As a carrier of application data, the action object is quickly
placed on the ValueStack so that its properties will be
accessible, via OGNL.
There are no infrastructural objects, such as Servlet API or
Struts 2 objects, on the ValueStack.
The ValueStack pretends to be a single object when OGNL
expressions are resolved against it.
The virtual object contains all the properties of all the
objects that have been placed on the stack.
If multiple the occurrences of the same property exist,
those lowest down in the stack are hidden by the
uppermost occurrence of a similarly named property.
6. Struts Tags
The tag API specifies the attributes and parameters
exposed by the tag.
Interfaces to the tag API are implemented in JSP, Velocity,
or FreeMarker.
While setting the attributes in a tag, If an attribute is of
type String, then its value is interpreted as a string literal in
JSP or Velocity.
If an attribute is some non-String type (i.e. points to some
property on the ValueStack), then the value of the attribute
is interpreted as an OGNL expression.
<s:property value="nonExistingProperty"
default="doesNotExist" /> displays “doesNotExist” when
nonExistingProperty doesn’t exist in ValueStack.
A string attribute to be interpreted as an OGNL expression
by using the %{expression} syntax.
7. Data Tags
Data tags let you get data out of the ValueStack or place variables
and objects onto the ValueStack.
They are the property, set, push, bean, and action tags.
The property tag provides a quick way of writing a property on
ValueStack or in ActionContext into the rendering HTML.
It has attributes value, default (used when values is null) and
escape (whether to escape HTML).
The set tag assigns a property to another name. It has attributes
name, scope (application, session, request, page, or action as
default),value (expresson of value).
A named object in ActionContext can be referenced as a toplevel
named object with an OGNL expression such as #myObject.
The push tag allows you to push properties onto the ValueStack
and has opening and closing tags containing toplevel properties
from ValueStack.
The bean tag is like a hybrid of the set and push tags. An
instance of an object can either be pushed onto the ValueStack
by default or set a top-level reference to it in the ActionContext.
8. Data Tags: Action Tag
Action tag allows to invoke another action from the view layer.
The executeResult attribute, allows to indicate whether the result
for the secondary action should be written into the currently
rendering page, and the name and namespace attributes, by
which the secondary action is identified to be fired.
By default, the namespace of the current action is used.
name: The action name, is of type string.
namespace: The action namespace of type string; defaults to
the current page namespace.
var: Reference name of the action bean for use later in the page.
executeResult: When set to true, executes the result of the
action (default value: false).
flush: When set to true, the writer will be flushed upon end of
action component tag (default value: true)
ignoreContextParams: When set to true, the request
parameters are not included when the action is invoked (default
value: false).
9. Control Tags: Iterator Tag
The iterator tag allows to loop over collections of objects.
It also provides the ability to define a variable in the
ActionContext, the iterator status, lets it determine certain basic
information about the current loop state such as loop odd or
even rows.
The value attribute specifies the object to be looped over and the
status specifies an IteratorStatus object placed in the action
context with the name as value of the attribute.
IteratorStatus object consists information such as the size,
current index, and whether the current object is in the even or
odd index in the list.
The if and else tags provide a familiar if else control logic.
They have one Boolean attribute called test, that is evaluated for
true or false.
<s:if test="user.age > 35">This user is too old.</s:if>
<s:elseif test="user.age < 35">This user is too young</s:elseif>
<s:else>This user is just right</s:else>
10. Miscellaneous Tags
The include tag allows to execute a Servlet API–style include,
allowing to include the output of any another web resource in
the currently rendering page.
The include tag takes the attribute name of the page, action,
servlet, or any reference able URL.
It provides native access to the ValueStack and a more extensible
parameter model.
The URL tag supports URL management from controlling
parameters to automatically persisting sessions in the absence of
cookies.
<s:url action="IteratorTag" var="myUrl">
<s:param name="id" value="2"/>
</s:url>
<a href='<s:property value="#myUrl" />'> Click Me </a>
The param tag specifies querystring parameters to be added to
the generated URL.
The includeParams attribute specifies whether parameters
from the current request are carried over into the new URL.
11. Miscellaneous Tags
The text tag is used to display language-specific text, such
as English or Spanish, based on a key lookup into a set of
text resources.
It retrieves a message value from the ResourceBundles
exposed through the framework’s own internationalization
mechanisms.
The i18n tag is used to manually specify ResourceBundle
to be used in the test tag.
<s:i18n
name="maning.chapterSix.myResourceBundle_tr">
In <s:text name="language"/>,
<s:text name="girl" var="foreignWord"/></s:i18n>
"<s:property value="#foreignWord"/>" means girl.
The param tags is a means for passing parameters into the
custom utility objects.
12. Results
The result is the encapsulation of the MVC view concerns of the
framework.
The Action class manages the application's state, while the
Result Type manages the view.
By default, the framework uses a result type which works with
JSPs to render the response pages.
The result, the dispatcher result, makes all the ValueStack data
available to the executing JSP page.
<action name="HomePage" class=". . . HomePage">
<result>/chapterEight/HomePage.jsp</result>
</action>
A result is the element into which you stuff the location of the
JSP page.
13. Predefined Result Types
The framework provides several implementations of
the com.opensymphony.xwork2.Result interface.
Chain Result Used for Action Chaining
Dispatcher Result Used for web resource integration, including JSP integration
FreeMarker Result Used for FreeMarker integration
HttpHeader Result Used to control special HTTP behaviors
RedirectResult Used to redirect to another URL (web resource)
Redirect ActionResult Used to redirect to another action mapping
Stream Result
Used to stream an InputStream (raw data) back to the browser
(usually for file downloads)
Velocity Result Used for Velocity integration
XSL Result Used for XML/XSLT integration
PlainText Result Used to display the raw content of a particular page (i.e jsp, HTML)
Tiles Result Used to provide Tiles integration
14. Chain Result
This result invokes an entire other action, complete with it's own interceptor
stack and result.
<package name="public" extends="struts-default">
<!-- Chain creatAccount to login, using the default parameter -->
<action name="createAccount" class="...">
<result type="chain">login</result>
</action>
<action name="login" class="...">
<!-- Chain to another namespace -->
<result type="chain">
<param name="actionName">dashboard</param>
<param name="namespace">/secure</param>
</result>
</action>
</package>
<package name="secure" extends="struts-default" namespace="/secure">
<action name="dashboard" class="..."> <result>dashboard.jsp</result>
</action> </package>
15. Dispatcher Result
Includes or forwards to a view (usually a jsp).
Behind the scenes Struts will use a RequestDispatcher,
where the target servlet/JSP receives the same
request/response objects as the original servlet/JSP.
Data can be passed between them using
request.setAttribute() - the Struts action is available.
<result name="success" type="dispatcher">
<param name="location">foo.jsp</param>
</result>
16. FreeMarker and Velocity Result
FreeMarker: Renders a view using the Freemarker template engine.
The FreemarkarManager class configures the template loaders so that
the template location can be either relative to the web root folder.
eg /WEB-INF/views/home.ftl or a a classpath resuorce.
eg /com/company/web/views/home.ftl.
<result name="success“ type="freemarker">
foo.ftl
</result>
Velocity: Using the Servlet container's JspFactory, this result mocks a
JSP execution environment and then displays a Velocity template by
streamming directly to the servlet output.
<result name="success" type="velocity">
<param name="location">foo.vm</param>
</result>
17. HttpHeader Result
A custom Result type for setting HTTP headers and status by
optionally evaluating against the ValueStack.
The result can also be used to send and error to the client.
<result name="success" type="httpheader">
<param name="status">204</param>
<param name="headers.a">a custom header value</param>
<param name="headers.b">more custom header value</param>
</result>
<result name="proxyRequired" type="httpheader">
<param name="error">305</param>
<param name="errorMessage">accessed through prozy</param>
</result>
18. Adding Spring to Struts 2
Spring can handle the objects created by Struts 2 by providing a
Spring extension of the Struts 2 ObjectFactory, the class that
creates each of the objects used in the framework.
A Spring plug-in is added to the framework providing a Spring
extension of the core ObjectFactory.
Spring provides the opportunity to manage the creation of any
objects that the struts framework creates.
The plug-in struts2-spring-plugin-2.0.9.jar and spring.jar are
added to the lib directory.
The Spring container is created using a Spring application-
context listener in the Spring JAR and is set up with the
following snippet from the web.xml file:
<listener>
<listener-
class>org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
19. Adding Spring to Struts 2
In order for the Spring to handle the objects, the objects are declared as
Spring beans in a Spring configuration file.
The ContextLoaderListener by default looks up for the config file in
/WEB-INF/applicationContext.xml.
Spring is given information to manage the object in the
applicationContext.xml.
By default, Spring beans are created as singletons, which won’t work for
Struts 2 actions, because they carry data related to each individual
request, hence scope is set to prototype force Spring to create a unique
instance for each request.
<bean id="portfolioService“
class="main.chapterNine.utils.PortfolioServiceJPAImpl"/>
<bean id="springManagedLoginAction"
class="main.chapterNine.Login“
scope="prototype">
<property name="portfolioService" ref="portfolioService"/>
</bean>
</beans>
20. Adding Spring to Struts 2
Spring creates one of its beans after referring to the Spring bean’s
ID from within the Struts 2 action mapping, as follows:
<action name="Login" class="springManagedLoginAction">
<result type="redirectAction">
<param name="actionName">AdminPortfolio</param>
<param name="namespace">/chapEight/secure</param>
</result>
<result name="input">/chapEight/Login.jsp</result>
</action>
The framework asks Spring for a bean going by the name of
springManagedLoginAction and Spring gladly returns that bean
with the PortfolioService injected and ready to go.
21. Validation Framework Architecture
The validation framework provides a more versatile and
maintainable solution to validation than the Validateable
interface.
The Validator is the stronger points of the validation
framework which is a reusable component in which the
logic of specific types of validations are implemented.
The three main components in the validation framework
are: the domain data, validation metadata, and the
validators.
DOMAIN DATA: The domain data is the properties of a
Struts 2 action.
These properties are assumed to hold the data for the
action to work with when it begins execution.
The domain data could also be implemented in various
ways such as via a ModelDriven action for instance.
22. Validation Framework Architecture
VALIDATION METADATA:
A middle component lies between the validators and the data
properties.
Such middle component is the metadata that associates individual data
properties with the validators that should be used to verify the
correctness of the values in those properties at runtime.
Many validators can be associated with each property, including zero if
that makes sense for the requirements.
The developer can map data properties to validators with XML files
(ActionClass-validations.xml)or with Java annotations.
VALIDATORS:
A validator is a reusable component that contains the logic for
performing some fine-grained act of validation.
The framework comes with a rich set of built-in validators such as
stringlength validator.
To validate the data by the validators, the properties need to be wired
up to the desired validators via some XML or Java annotations.
When the validation executes, each property is validated by the set of
validators with which it’s been associated by the metadata layer.
23. Basic Validation
The actions extend ActionSupport class, which implements a couple of
interfaces that play an important role in validation.
The interfaces are com.opensymphony.xwork2.Validateable and
com.opensymphony.xwork2.ValidationAware.
Validateable exposes the validate() method, in which resides the
validation code, and ValidationAware exposes methods for storing
error messages generated when validation finds invalid data.
These interfaces work in tandem with the workflow interceptor.
When the workflow interceptor fires, it first checks to see whether the
action implements Validateable.
If it does, the workflow interceptor invokes the validate() method.
If the validation code finds that some piece of data isn’t valid, an error
message is created and added to one of the ValidationAware methods
that store error messages.
When the validate() method returns, the workflow interceptor and still
has another task, it calls ValidationAware’s hasErrors() method to see if
there were any problems with validation.
If errors exist, the workflow interceptor intervenes by stopping further
execution of the action by returning the input result, which returns the
user back to the form that was submitted.
24. Validation Framework
The validation interceptor, which follows the conversionError interceptor in
the defaultStack, provides the entry point into this validation process.
When the validation interceptor is fired, it conducts all the validation that’s
been defined via the validation metadata.
If the validation is not true then an error is added to the ValidationAware
methods.
Note that both conversion errors and validation errors are collected by
ValidationAware.
Whether errors are added or not, it still proceeds to the next interceptor, the
workflow interceptor.
The workflow interceptor first invokes the validate() method, if exposed by the
current action in basic validation.
The workflow interceptor then checks for errors, by checking the
ValidationAware method hasErrors().
If there are no errors, it passes control on to the rest of the action invocation
process.
If errors are found, workflow is diverted and it returns to the input page and
present the user with error messages.
The basic validation methods and the validation framework can be used at the
same time.
25. Sample Validation Metadata File
<validators>
<field name="password">
<field-validator type="requiredstring">
<message>You must enter a value for password.</message>
</field-validator>
</field>
<field name="username">
<field-validator type="stringlength">
<param name="maxLength">8</param>
<param name="minLength">5</param>
<message>While ${username} is a nice name, a valid username must
be between ${minLength} and ${maxLength} characters long.
</message>
</field-validator>
</field>
<validator type="expression">
<param name="expression">username != password</param>
<message>Username and password can't be the same.</message>
</validator>
</validators>
26. Field Validators
Field validators are validators that operate on an
individual field or data property.
The fields are the HTML form fields that submitted
the request.
The field-validator elements are inside the field
element to declare which validators should validate
this piece of data.
A field element can have more than one field
validators.
27. Non Field Validators
Non field validators don’t apply logic targeted at a
specific field.
Such validators apply to the whole action and often
contain checks that involve more than one of the field
values.
The built-in validators only offer one instance of a
nonfield validator: the expression validator.
The expression validator allows to embed an OGNL
expression containing the logic of the validation to
perform and resolves against the ValueStack.
28. The Message Element
The message element is used to specify the message that the
user should see in the event of a validation error.
OGNL expressions can be used to make the messsage dynamic,
and are resolved againist the ValueStack.
OGNL in the XML files uses the $ rather than the % sign that’s
normally used in OGNL.
The message can be externalize in a resource bundle as localized
messages.
The ActionSupport implements the TextProvider interface to
provide access to localized messages, and can be accessed by the
TextProvider’s getText() method from the validate() method.
The message element doesn’t have a text body but, it sets the key
attribute to a value to be used to look up the message via the
TextProvider implementation in the ActionClass.properties file.
29. Struts 2 Built-in Validators
Validator Params Function
required None Verifies that value is non-null.
requiredString trim Verifies value is non-null, & not an empty string
stringlength trim,min,max Verifies string length falls within the params
int min, max Verifies integer value falls between min & max
double minIn,maxIn,
minEx, maxEx
Verifies double value falls between the
inclusively or exclusively specified parameters.
date min, max Verifies date value falls between min & max
email None Verifies email address format.
url None Verifies URL format.
fieldexpression expression Evaluates OGNL expression against ValueStack.
expression expression Same as fieldexpression, used at actionlevel.
Visitor Context,
appendPrefix
Defers validation of a domain object property,
to validations made local to domain object.
regex exp, trim,
caseSensitive
Verifies String conforming to regular expression
30. Writing a Custom Validator
All validators are obligated to implement the Validator or
FieldValidator interface.
One can extend either ValidatorSupport or FieldValidatorSupport,
from the validators package to write a custom tag.
A nonfield validator, which performs a validation check involving more
than one field, is written by extend ValidatorSupport class.
The validation code is specified in the validate() method.
The validator also needs to create JavaBeans properties to match all
parameters to be exposed to the user.
The following snippet shows parameter passing from the XML file to
the property:
<field-validator type="passwordintegrity">
<param name="specialCharacters">$!@#?</param>
<message>Password must have 1 of foll "${specialCharacters}".
</message>
</field-validator>
31. Writing a Custom Validator
Most of the helper methods are actually defined in
ValidatorSupport, which is extended by FieldValidatorSupport.
The Field name and value is retrieved by calling helper methods,
getFieldName() and getFieldValue() defined in
FieldValidatorSupport class.
Error storing methods are inherited from support classes.
The custom validators are declared in an application-local
validators.xml file, placed in the root of the classpath—directly
under the src folder in the WEB-INF/classes/.
<validators>
<validator name="passwordintegrity"
class="manning.utils.PasswordIntegrityValidator"/>
</validators>
32.
33. Apache Struts 2 Framework
An initial request goes to the Servlet container (such as Jetty or
Resin) which is passed through a standard filter chain.
The chain includes the (optional) ActionContextCleanUp
filter, which is useful when integrating technologies such as
SiteMesh Plugin.
Next, the required FilterDispatcher is called, which in turn
consults the ActionMapper to determine if the request should
invoke an action.
If the ActionMapper determines that an Action should be
invoked, the FilterDispatcher delegates control to the
ActionProxy.
The ActionProxy consults the framework Configuration Files
manager (initialized from the struts.xml file).
Next, the ActionProxy creates an ActionInvocation, which is
responsible for the command pattern implementation which
includes invoking any Interceptors (the before clause) in
advance of invoking the Action itself.
Once the Action returns, the ActionInvocation is responsible
for looking up the proper result associated with the Action result
code mapped in struts.xml.
34. Apache Struts 2 Framework
The result is then executed, which often (but not always, as is the case
for Action Chaining) involves a template written in JSP or FreeMarker
to be rendered.
Some of those components will work with the ActionMapper to
render proper URLs for additional requests.
All objects in the architecture (Actions, Results, Interceptors, and so
forth) are created by an ObjectFactory which is pluggable.
One can provide his own ObjectFactory for any reason that requires
knowing when objects in the framework are created. A popular
ObjectFactory implementation uses Spring as provided by the Spring
Plugin.
Interceptors are executed again (in reverse order, calling the after
clause).
Finally, the response returns through the filters configured in the
web.xml.
If the ActionContextCleanUp filter is present, the FilterDispatcher
will not clean up the ThreadLocal ActionContext.
If the ActionContextCleanUp filter is not present, the
FilterDispatcher will cleanup all ThreadLocals.