In my previous
article Aggregating
Actions in Struts, I have given a brief idea of how to create action
aggregations. An action aggregation is a grouping of a set of related actions
into a single unified action in order to eliminate the need to write a new
action class for every action. In this part, I will walk through a full-fledged
practical example of each of the types presented in Part I namely the DispatchAction, LookupDispatchAction, MappingDispatchAction
and ActionDispatcher along with two
new aggregations which slightly differ in syntax but are more efficient than
the previous ones. These are EventDispatchAction
and EventActionDispatcher. I call these
new actions as “Event Aggregating Actions” and the ones in Part I as “Non-Event
Aggregating Actions.”
Now let’s go into some details.
Non-Event Aggregating Actions
DispatchAction
How to create one?
1.
Extend your action class from DispatchAction.
// src/CRUDDispatchAction.java
/**
* This is an example class for
demonstrating DispatchAction
* functionality. Observe here
that we are extending our
* action class not from Action
but from DispatchAction.
*
* @author Praveen Babu Kusuma
* @version 1.0.0
*
* http://www.javahome.co.nr
* http://praveen.awardspace.com
*/
public final class CRUDDispatchAction extends DispatchAction {
public ActionForward
create(ActionMapping mapping,
ActionForm form,
HttpServletRequest
request,
HttpServletResponse response)
throws Exception {
// Code for Create
Employee follows
System.out.println("I
am in CRUDDispatchAction - create");
return (
mapping.findForward("success") );
}
...
}
2.
Define a query string parameter or a hidden variable (‘methodToCall’
in our examples)
// dispatchaction.jsp
<tr>
<td>
<html:link action="crudDispatchAction?methodToCall=create"><b>C</b>reate
an Employee record</html:link>
</td>
</tr>
3.
Set the parameter’s value to the desired function name of
the action class (create, read etc).
// dispatchaction.jsp
<tr>
<td>
<html:link action="crudDispatchAction?methodToCall=create"><b>C</b>reate
an Employee record</html:link>
</td>
</tr>
What are the advantages?
- If you observed carefully, we have created only one action class (CRUDDispatchAction) instead of four different classes for each of the Create, Read, Update and Delete actions. This is why aggregating actions are for.
- Easy to use.
- Cleaner code syntax.
What are the disadvantages?
- We cannot extend our CRUDDispatchAction from any custom base class and still have the aggregation.
- Observe that we have used html:link in dispatchaction.jsp and a query string variable appended to the URL. So, the method name is visible in the URL. In order to avoid this, we need to set it using javascript.
- Cannot attach events. More about this later.
LookupDispatchAction
How to create one?
- Extend your action class from LookupDispatchAction.
// src/CRUDLookupDispatchAction.java
/**
* This is an example class for
demonstrating CRUDLookupDispatchAction
* functionality. Observe here
that we are extending our
* action class not from Action
but from LookupDispatchAction.
*
* @author Praveen Babu Kusuma
* @version 1.0.0
*
* http://www.javahome.co.nr
* http://praveen.awardspace.com
*/
public final class CRUDLookupDispatchAction extends LookupDispatchAction {
protected Map
getKeyMethodMap() {
Map map = new
HashMap();
map.put("button.create",
"create");
map.put("button.read",
"read");
map.put("button.update",
"update");
map.put("button.delete",
"delete");
return map;
}
public ActionForward
create(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
// Code for Create
Employee follows
System.out.println("I
am in CRUDLookupDispatchAction - create");
return (
mapping.findForward("success") );
}
...
}
- Write an html:form and change the action name to the desired action mapping name.
// lookupdispatchaction.jsp
<html:form action="crudLookupDispatchAction">
...
- Set the ‘property’ attribute of the submit button to parameter name which should hold the method name to call.
// lookupdispatchaction.jsp
<tr>
<td
align="center">
<html:submit property="methodToCall"><bean:message
key="button.create"/></html:submit>
</td>
</tr>
What are the advantages?
- Multiple submit buttons within the same form only differing in their name.
- The actual method to call is mapped in the action class.
- Button names can be set in a properties file.
What are the disadvantages?
- We cannot extend our CRUDLookupDispatchAction from any custom base class and still have the aggregation.
- Change to the button name requires JVM restart.
- Extra overhead of creating a separate method for button-to-method name mapping.(getKeyMethodMap() in our example)
- Cannot attach events. More about this later.
MappingDispatchAction
How to create one?
- Extend your action class from MappingDispatchAction
// src/CRUDMappingDispatchAction.java
/**
* This is an example class for
demonstrating MappingDispatchAction
* functionality. Observe here
that we are extending our
* action class not from Action
but from MappingDispatchAction.
*
* @author Praveen Babu Kusuma
* @version 1.0.0
*
* http://www.javahome.co.nr
* http://praveen.awardspace.com
*/
public final class CRUDMappingDispatchAction extends MappingDispatchAction {
public ActionForward
create(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
// Code for Create
Employee follows
System.out.println("I
am in CRUDMappingDispatchAction - create");
return (
mapping.findForward("success") );
}
...
}
- Define different mappings for each action as needed.
// struts-config.xml
<!-- MappingDispatchAction mappings Starts -->
<!-- MappingDispatchAction
is used when the mappings differ
(observe that the
number/type of attributes differ for each action) -->
<action path="/createMappingDispatchAction"
type="com.CRUDMappingDispatchAction"
scope="request"
parameter="create"
>
<forward
name="success" path="/success.jsp"/>
</action>
<action
path="/readMappingDispatchAction"
type="com.CRUDMappingDispatchAction"
scope="session"
parameter="read"
>
<forward
name="success" path="/success.jsp"/>
</action>
<action
path="/updateMappingDispatchAction"
type="com.CRUDMappingDispatchAction"
scope="request"
input="/mappingdispatchaction.jsp"
parameter="update"
>
<forward
name="success" path="/success.jsp"/>
</action>
<action
path="/deleteMappingDispatchAction"
type="com.CRUDMappingDispatchAction"
scope="request"
name="employeeForm"
parameter="delete"
validate="false"
>
<forward
name="success" path="/success.jsp"/>
</action>
<!--
MappingDispatchAction mappings Ends -->
- Set the mapping name to the action attribute of html:link
// mappingdispatchaction.jsp
<tr>
<td>
<html:link action="createMappingDispatchAction"><b>C</b>reate
an Employee record</html:link>
</td>
</tr>
<tr><td> </td></tr>
<tr>
<td>
<html:link action="readMappingDispatchAction"><b>R</b>ead
an Employee details</html:link>
</td>
</tr>
<tr><td> </td></tr>
<tr>
<td>
<html:link action="updateMappingDispatchAction"><b>U</b>pdate
Employee details</html:link>
</td>
</tr>
<tr><td> </td></tr>
<tr>
<td>
<html:link action="deleteMappingDispatchAction"><b>D</b>elete
Employee record</html:link>
</td>
</tr>
What are the advantages?
- Single action class can be used for multiple action mappings. The attribute of the action mapping can be added or removed and a new mapping can be created while the action class remains the same.
- Useful in places where there are 2 actions, one of which needs a formbean while the other does not but both uses the same Action class. In such cases, two new action mappings can be created one with ‘name’ attribute and one without and the same Action class name can be used.
What are the disadvantages?
- We still cannot extend our CRUDMappingDispatchAction from any custom base class and have the aggregation intact.
- Cannot attach events. More about this later.
ActionDispatcher
How to create one?
- Extend your action class from Action. Do not confuse about the inclusion of BaseAction.java. I have just moved the common code part to this BaseAction and extended my action class from it.
// src/CRUDActionDispatcher.java
/**
* This is an example class for
demonstrating ActionDispatcher
* functionality. Observe here
that we are extending our
* action class from BaseAction
where the session validation
* is done.
* <p>
* See BaseAction for more
details
* <p>
* If we use any other
aggregating action then we cannot
* use session validation as we
cannot extend our custom class
* (BaseAction in this case).
This is the biggest strength of ActionDispatcher
* type of aggregation and the
biggest weakness of all other types.
*
* @author Praveen Babu Kusuma
* @version 1.0.0
*
* http://www.javahome.co.nr
* http://praveen.awardspace.com
*/
public final class CRUDActionDispatcher extends BaseAction {
public ActionForward create(ActionMapping
mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
// Code for Create
Employee follows
System.out.println("I
am in CRUDActionDispatcher - create");
return (
mapping.findForward("success") );
}
...
}
// src/BaseAction.java
/**
* This is an example class where
session validation
* is done normally. But if you
look at closely we have
* actually written our ActionDispatcher
code here and
* simply extended our example
class for demonstrating
* ActionDispatcher functionality
from this class.
* <p>
* This is the place where every
time the session is
* checked. A sample code snippet
could be :
*
* <code>
* session =
request.getSession(false); // which returns the existing session
* if( session != null ) {
* // allow the user in
* } else {
* // throw him/her out to the relogin page
* }
* </code>
* <p>
* This is perhaps the most
crucial part of any web application
* since this is to be done for
every class or every class is to be
* extended from this kind of
(Base) class.
*
* @author Praveen Babu Kusuma
* @version 1.0.0
*
* http://www.javahome.co.nr
* http://praveen.awardspace.com
*/
public class BaseAction extends
Action {
protected ActionDispatcher dispatcher =
new
ActionDispatcher(this, ActionDispatcher.DEFAULT_FLAVOR);
public ActionForward
execute(ActionMapping mapping,
ActionForm
form,
HttpServletRequest request,
HttpServletResponse
response)
throws Exception {
// This is a default
method and is mandatory since we are
// not extending any
dispatcher action class(eg, extends ActionDispatcher{ }).
// This method will
handle the dispatching of the action to the appropriate method.
// Code for Session
Validation follows
System.out.println("I
am in BaseAction - execute");
return dispatcher.execute(mapping, form, request, response);
}
}
- Declare a variable of type ActionDispatcher as follow (see above code snippet):
protected ActionDispatcher dispatcher =
new
ActionDispatcher(this, ActionDispatcher.DEFAULT_FLAVOR);
- Add the following line as the last line of your default ‘execute’ method. Note that this is not needed for other methods. (see above code snippet)
return
dispatcher.execute(mapping, form, request, response);
- Define a query string parameter or a hidden variable (‘methodToCall’ in our examples)
// actiondispatcher.jsp
<tr>
<td>
<html:link
action="crudActionDispatcher?methodToCall=create"><b>C</b>reate
an Employee record</html:link>
</td>
</tr>
- Set the parameter’s value to the desired function name of the action class (create, read etc).
// actiondispatcher.jsp
<tr>
<td>
<html:link
action="crudActionDispatcher?methodToCall=create"><b>C</b>reate
an Employee record</html:link>
</td>
</tr>
What are the advantages?
- Observe that we have extended our action class (CRUDActionDispatcher) from our custom base class (BaseClass) which is what we wanted in order to decouple the session validation code into the BaseClass.
- Only a couple of changes to the code avoid the need to extend from a subclass.
- Easy to use.
What are the disadvantages?
- We cannot attach events to the controls.
Event Aggregating Actions
If you are
familiar with swing programming you might have heard of events. An event is
nothing but a piece of code that is executed when, say, a button is clicked, a
linked is clicked etc. In all the above types of actions we have defined a
parameter (methodToCall) which is in turn mapped to the action name. This is a
good feature for security concerns. However, we need a way to have the method
name different from the actual method called, at the same time eliminating the
need to define a hidden variable or a query string. This is where the Event
Aggregating Actions comes into the picture.
There are two important
types of Event Aggregating Actions as said earlier, namely, EventDispatchAction and EventActionDispatcher
Let’s see each
one of them in detail.
EventDispatchAction
How to create one?
- Extend your action from EventDispatchAction
// CRUDEventDispatchAction.java
/**
* This is an example class for
demonstrating EventDispatchAction
* functionality. Observe here
that we can also extend our class from
* BaseAction class like in
ActionDispatcher example and move our
* session validation to that
class.
* <p>
* The EventActionDispatcher as
the name suggests is used to attach
* events to the html controls.
This type of aggregation eliminates the
* need to define a
parameter(eg., methodToCall). We havent used any
* hidden parameters to combine
an event to a method. This event-to-method
* mapping is removed using
EventActionDispatcher.
*
* We can also use an Alias for
the method name and map it to the method name
* as can be seen in the
createAlias event.
*
* @author Praveen Babu Kusuma
* @version 1.0.0
*
* http://www.javahome.co.nr
* http://praveen.awardspace.com
*/
public final class CRUDEventDispatchAction extends EventDispatchAction {
public ActionForward
create(ActionMapping mapping,
ActionForm
form,
HttpServletRequest
request,
HttpServletResponse
response) throws Exception {
// Code for Create
Employee follows
System.out.println("I
am in CRUDEventDispatchAction - create");
return (
mapping.findForward("success") );
}
...
}
- Set the action name to the action attribute of html:link
// eventdispatchaction.jsp
<tr>
<td>
<html:link action="crudEventDispatchAction?createAlias=create"><b>C</b>reate an
Employee record</html:link>
</td>
</tr>
- Either directly use the method name to call as a query string parameter, or use an alias.
// eventdispatchaction.jsp ( Direct calling )
<tr>
<td>
<html:link
action="crudEventDispatchAction?update"><b>U</b>pdate
Employee details</html:link>
</td>
</tr>
// eventdispatchaction.jsp ( Calling using an alias )
<tr>
<td>
<html:link action="crudEventDispatchAction?createAlias=create"><b>C</b>reate
an Employee record</html:link>
</td>
</tr>
What are the advantages?
1.
Events can be attached to controls directly.
2.
No need to use a separate hidden variable or a query
string for mapping the method name to the actual method to call.
3.
Actual method to call can be aliased with a pseudo
name.
What are the disadvantages?
- The need to extend the class from EventDispatchAction and hence cannot extend our class from a custom base class.
EventActionDispatcher
How to create one?
- Extend your action from Action, similar to the case in ActionDispatcher. This is what is desirable for us.
// CRUDEventActionDispatcher.java
/**
* This is an example class for
demonstrating EventActionDispatcher
* functionality. Observe here
that we can also extend our class from
* BaseAction class like in
ActionDispatcher example and move our
* session validation to that
class.
* <p>
* The EventActionDispatcher as
the name suggests is used to attach
* events to the html controls.
This type of aggregation eliminates the
* need to define a
parameter(eg., methodToCall). We havent used any
* hidden parameters to combine
an event to a method. This event-to-method
* mapping is removed using
EventActionDispatcher.
*
* We can also use an Alias for
the method name and map it to the method name
* as can be seen in the
createAlias event.
*
* @author Praveen Babu Kusuma
* @version 1.0.0
*
* http://www.javahome.co.nr
* http://praveen.awardspace.com
*/
public final class CRUDEventActionDispatcher extends Action {
protected ActionDispatcher
dispatcher = new EventActionDispatcher(this);
public ActionForward
execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception
{
// This method will
handle the dispatching of the action to the appropriate method.
System.out.println("I
am in CRUDEventActionDispatcher - execute");
return
dispatcher.execute(mapping, form, request, response);
}
...
}
- Set the action name to the action attribute of html:link.
// eventactiondispatcher.jsp
<tr>
<td>
<html:link action="crudEventActionDispatcher?createAlias=create"><b>C</b>reate
an Employee record</html:link>
</td>
</tr>
- Either directly use the method name to call as a query string parameter, or use an alias.
// eventactiondispatcher.jsp ( Direct calling )
<tr>
<td>
<html:link
action="crudEventActionDispatcher?delete"><b>D</b>elete
Employee record</html:link>
</td>
</tr>
// eventactiondispatcher.jsp ( Calling using an alias )
<tr>
<td>
<html:link
action="crudEventActionDispatcher?createAlias=create"><b>C</b>reate
an Employee record</html:link>
</td>
</tr>
What are the advantages?
- Events can be attached to controls directly.
- No need to use a separate hidden variable or a query string for mapping the method name to the actual method to call.
- Actual method to call can be aliased with a pseudo name.
What are the disadvantages?
- None.
Note that the use of true
(read=true) while using the query string variable to specify the event name is
optional. Also, note that this variable is different from the one we used
earlier (methodToCall). Here we directly specify the event name or its alias
not the variable name or the actual method to call.
No comments:
Post a Comment