Professional Documents
Culture Documents
This means that the front controller is out front and is the first component to act in the
processing.
The controller’s job is to map requests to actions.
The role of controller is played by FilterDispatcher in Struts2
The important object is a servlet filter that inspects each incoming request and determines
which action should handle the request.
Model – Action:
View – Result:
When the request arrives, FilterDispatcher selects the appropriate action to handle the request.
There is a stack of interceptors in front of the action.
The invocation of action must travel through this stack.
These interceptors are invoked both before and after the action, though we should note that
they actually fire after the result has executed.
Important thing is that the interceptor allows common, cross-cutting tasks to be defined in
clean, reusable components that you can keep separate from your action code.
ValueStack is a storage area that holds all of the data associated with processing a request.
Rather than passing the data around, Struts2 keeps it in a convenient, central location –
ValueStack.
OGNL is an expression language that allows you to reference and manipulate the data on
ValueStack.
The ActionContext contains all of the data that makes up the context in which an action occurs.
ActionContext has ValueStack and also includes stuff the framework itself will use internally,
such as request, session, and application maps from Servlet API.
By default, Struts2 looks for URLs ending in .action, but you can configure the framework to look
for .do (the Struts1.x default extension) or even no extension at all.
There are two ways declaring architecture – XML based configuration or Java annotations.
<struts>
<package name=”login” namespace=”/login” extends=”struts-default”>
<action name="Login" class="manning.Login">
<result>/AccountPage.jsp</result>
<result name="input">/Login.jsp</result>
</action>
</package>
<struts>
The framework uses struts.xml as the entry point. The file resides in the Java classpath and must
be created by the developer. While it is possible to declare all your components in struts.xml,
developers more commonly use this file only to include secondary XML files.
Using annotations:
@Results({
@Result(name="input", value="/RegistrationSuccess.jsp" )
@Result(value="/RegistrationSuccess.jsp" )
})
public class Login implements Action {
public String execute() {
//Business logic for login
}
}
Many commonly used Struts2 components do not need to be declared by the developers. These
predefined components are the part of Struts2 intelligent defaults. These components are
declares in struts-default.xml, found in struts2-core.jar.
Web.xml of the webapp:
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
<init-param>
<param-name>actionPackages</param-name>
<param-value>manning</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
actionPackages parameters is necessary if you are going to use annotations in your package.
struts.xml
<struts>
<constant name="struts.devMode" value="true" />
<package name="default" namespace="/" extends="struts-default">
<action name="Menu">
<result>/menu/Menu.jsp</result>
</action>
</package>
<include file="manning/chapterTwo/chapterTwo.xml"/>
<include file="manning/chapterThree/chapterThree.xml"/>
...
<include file="manning/chapterEight/chapterEight.xml"/>
</struts>
o Constant element can be used to set framework properties. You can also do this with
property files.
o struts.xml is a good place to define global actions in default package.
o Include element can be used to pull xml files into declarative architecture.
While many Struts2 actions will implement the Action interface, they are only obligated to meet
an informal contract.
The execute() method contains business logic and returns a string that indicates which of its
results should render the result page.
Domain data is always stored in the action. This is because it allows convenient access to data
from action’s execute() method.
So that the rest of the framework can access data, the action object is itself placed in the
ValueStack.
The mechanics of ValueStack are such that all properties of the action will then be exposed as
top level properties of ValueStack.
Actions:
Struts2 creates a new instance of action for every request.
Actions and other components can be grouped together into packages. These packages are
similar to Java packages.
You can set following attributes in the package:
Note that you can give the same namespace to more than one packages.
If you don’t set the namespace attribute, your actions will go into the default namespace. The
default namespace sits beneath all of the other namespaces waiting to resolve requests that
don’t match any explicit namespace.
Note that the default namespace is actually the empty string “”. You can define a root
namespace such as “/”. The root namespace is treated as all other explicit namespaces and must
be matched.
Most actions implement com.opensymphony.xworks2.Action which defines only
String execute() throws Exception method. It also defines some string constants like ERROR,
INPUT, LOGIN etc.,
ActionSupport class is a convenience class that provides default implementation of Action
interface and several other useful interfaces. If your action class extends this class, they
automatically gain the use of default implementations of several important interfaces.
Action Support provides a quick form of basic validation.
This class works with workflow interceptor to perform basic validation.
When workflow interceptor fires, it will look for validate() method on the action (through
com.opensymphony.xwork2.Validateable interface).
If the piece of data doesn’t validate, we create and store an error via methods provided by the
ActionSupport superclass, such as addFieldError() or addActionError().
After all validation logic has executed, control returns to workflow interceptor. The workflow
interceptor will check to see whether any error messages were generated by validation logic.
Workflow interceptor will abort the request processing and return the user back to input form.
Using Resource Bundles for message text:
Data transfer:
File Upload:
Interceptors:
Servlet-Config Interceptor:
o The servlet-config interceptor provides a clean way of injecting various objects from the
Servlet API into your actions.
o This interceptor works by setting the various objects on setter methods exposed by
interfaces that action must implement.
o Following interfaces are available for retrieving various objects related to servlet
environment:
ServletContextAware – ServletContext
ServletRequestAware – ServletRequest
ServletResponseAware – ServletResponse
ParameterAware – Sets a map of request parameters
RequestAware – sets a map of request attributes
SessionAware – sets a map of the session attributes
ApplicationAware – sets a map of application aware attributes
PrincipalAware – Sets the principal object
o Each of these interfaces contains one method – a setter – for resource in question.
Prepare Interceptor:
o When prepare interceptor fires, it looks for prepare() method (and if your action
implements preparable interface). If your action is preparabale, prepare() method is
executed. This allows for any sort of preprocessing to occur.
Exception Interceptor
Token and Token-session Interceptors:
o These interceptors can be used as a part of the system to prevent duplicate form
submission.
ExecAndWait Interceptor
<package name="struts-default">
<interceptors>
<interceptor name="execAndWait" class="ExecuteAndWaitInterceptor"/>
<interceptor name="exception" class="ExceptionMappingInterceptor"/>
<interceptor name="fileUpload" class="FileUploadInterceptor"/>
…..
<interceptor-stack name="defaultStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="servlet-config"/>
</interceptor-stack>
</interceptor>
<default-interceptor-ref name="defaultStack"/>
</package>
OGNL
We can implement customized type convertors. Just make a class extend StrutsTypeConvertor
and implement two methods convertFromString() and convertToString().
ActionContext
We already know that our action is placed on ValueStack. ValueStack itself is store in something
called ActionContext.
We know that OGNL targets objects on ValueStack.
In reality, OGNL expressions can resolve against any set of objects. ValueStack is one among
them and the default one.
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 of the application specific data will be held in ValueStack.
The other objects on ActionContext are all maps of important sets of data.
Struts2 Tags:
Strut2 tag API provides functionality to dynamically create robust web pages.
Struts2 comes with data tags, control-flow tags, UI and miscellaneous tags.
Data tags focus on ways to extract data from the ValueStack and/or set values in the ValueStack.
If the attribute is of type String, then the value written into the attribute, in the actual JSP page
is interpreted as a String literal. If an attribute is some non-string type, then value written into
the attribute is interpreted as an OGNL expression.
You can force a string attribute to be interpreted as an OGNL expression using the %
{expression}. JSTL EL ${expression}
Data Tags:
o Property Tag – Used for writing a property into the rendering HTML.
<s:property value="user.username"/>.
o Set Tag – Used to assign a property to another name.
<s:set name="username" value="user.username"/>
Can specify scope – application, session, request, page or action. Default action. i.e,
you can access the property now as #username.
o Push Tag – Allows you to push properties onto the ValueStack.
o Bean Tag – is like a hybrid of set and push. You can create an instance of object and
push it to ValueStack. The value will remain on the ValueStack for the duration of the
tag.
Now we will see how we can do the same by creating a top-level reference to the bean
in theActionContext. We do this using the following code.
<s:bean name="vaannila.CurrencyConverter" var="converter">
<s:param name="dollars" value="100"></s:param>
</s:bean>
Dollars = <s:property value="#converter.rupees" /> Rupees
o Action tag – To invoke another action from view layer.
Control Tag
o Iterator tag <s:iterator value="songs" status="songStatus">
o If and else tags
URL tag can be used to automatically persist session in absence of cookies.
Text tag – Is used to retrieve language specific text from resource bundles.
I18n tag – used to set resource bundle. Resource Bundle is used only for duration of the tag.
Results
As an alternative to configuring results locally to specific actions, you can also configure results
globally – for an entire package.
When an action returns a control string, the framework consults the set of results as defined
locally to action. If no results with the name are found, it then consults the globally defined set
of results.
Note, locally defined results will override a globally defined results during the lookup process
<global-results>
<result name="error">/chapterFour/Error.jsp</result>
</global-results>
One of the strong points of validation framework is the Validator, a reusable component in
which the logic of specific types of validations is implemented.
There are three main components of validation framework:
o Domain data – Data to validate
o Validation Metadata – used to associate individual data properties with validators
o Validators – Actual validation is performed here
Validation interface also makes use of ValidationAware interface to store errors and the
workflow interceptor to route back to input page, if necessary.
One more interceptor, Validation Interceptor, is used as the entry point of validation
framework.
You can use basic validation and validation framework together. When you use defaultStack,
both the validation and workflow interceptors fire. First, the validation interceptor runs all
validations that you define with the validation framework metadata. Then, when the workflow
interceptor runs, it still checks to see if your action implements the validate() method. When the
second phase of workflow interceptor checks for errors, it doesn’t care or knows who created
them. The effect is same.
Validation metadata must be placed in a XML file named actionclassname-validations.xml and
placed in the package next to the action class.
<validators>
<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>
<field name="email">
<field-validator type="requiredstring">
<message>You must enter a value for email.</message>
</field-validator>
<field-validator type="email">
<message key="email.invalid"/>
</field-validator>
</field>
<validator type="expression">
<param name="expression">username != password</param>
<message>Username and password can't be the same.</message>
</validator>
</validators>
if ( !digitMatcher.find() ) {
addFieldError( fieldName, object );
}
}
private String specialCharacters;
//Getter and setter omitted
}
The logic is placed in the validate() method
Developer must create all JavaBean properties to match all the parameters that should
be exposed to the user.
<field-validator type="passwordintegrity">
<param name="specialCharacters">$!@#?</param>
<message>
Your password must contain one letter, one number, and one of
the following "${specialCharacters}".
</message>
</field-validator>
We declare our own custom validators in an application local validators.xml file and
place it in the root of our classpath.
<validators>
<validator name="passwordintegrity"
class="manning.utils.PasswordIntegrityValidator"/>
</validators>
Short-circuiting validations:
<field-validator type="stringlength" short-circuit="true">
If validation performed by this validator fails, further validators declared for the field are
not performed.
Internationalization
Java support for internationalization centers around two classes from java.util package –
ResourceBundle and Locale.
ResourceBundle is a locale-sensitive object.
The resources that are found in these ResourceBundles are message texts.
ResourceBundle is an abstract class. Subclasses of ResourceBundle can manage their
resources in any way they want.
Most commonly used subclass is PropertyResourceBundle, which loads its resources
from plain text properties file.
We should create one properties file for each language-specific version of the text
resource.
Regardless of the number of supported locales, all of the properties files that share the
same root bundle name belong to a single bundle.
Java native code for working with resource bundles:
How does getBundle() method know which properties file to use – First, it searches for Java
properties file that matches the bundle name parameter that it receives. After that, it also
searches for Java class implementation of the bundle.
struts.custom.i18n.resources=global-messages,manning.utils.otherBundle
Framework automatically determines default locale at the start of the request processing.
This determination is made from the HTTP header in the request.
The i18n interceptor checks to see if the request contains a parameter named
request_locale. If this parameter exists in the request, then this value is set on
ActionContext, thus overriding the default locale choice made by the framework.
You can use this parameter to have user select a locale interactively.
As ActionContext exists for a single request, when user sets the request_locale parameter
once, i18n interceptor will store it in session scoped attribute named,
WW_TRANS_I18N_LOCALE.
In Struts 1 DispatchAction helps us in grouping a set of related functions into a single action. In
Struts 2 all the Actions by default provide this functionality. To use this functionality we need to
create different methods with the similar signature of the execute() method, only the name of
the method changes.
We need to specify which method in the action class will be called while configuring the action
mapping. A separate action mapping needs to be created for each method in the action class.
The following action mapping is done in the struts.xml file.
As you can see we have replaced all the method names with an asterisk symbol. The word that
matches for the first asterisk will be substituted for the method attribute. So when the request
URL is "addUser" the add() method in the UserAction class will be invoked. Method name will be
submitted for {1}.
Using tokens to prevent duplicate form submits
- Prepare the web page with a unique token embedded as a hidden field.
- Stash this unique token in the user session.
- Return the page to user browser.
- When the form is submitted, the two tokens are compared.
- If the tokens do not match, invalid.token result is returned.
<s:form>
<s:token/>
<s:textfield key="coinToss"/>
<s:submit action="flipCoin"/>
</s:form>
<interceptor-stack name="waitStack">
<interceptor-ref name="defaultStack" />
<interceptor-ref name="execAndWait" />
</interceptor-stack>
Note that the execAndWait interceptor is declared last. It’s important that execAndWait be the
last interceptor, because it stops the execution and no further interceptors will be called. In the
thread created by the execAndWait interceptor, only the action is executed, so any interceptors
after the execAndWait will never be run.
The most interesting element of the web page is the meta tag in the head section. This instructs
the web browser to rerequest the URL that resulted in this wait page every two seconds. As long
as the processing of the action is going on, the waitPage view is returned, which sets the
browser timer to refresh again in two seconds. Eventually, the action finishes and a page refresh
returns the result from the action by rendering the resultsPage in the browser.
<html>
<head>
<title>Please wait</title>
<meta http-equiv="refresh" content="2;url=<s:url/>"/>
</head>
<body>
Processing your request. Please wait a moment...
</body>
</html>