|
|
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 2 of 2
The XML definition above instructs the Spring container to:
helloOffspringCommand object.
helloOffspringController. Its constructor arguments are the name of the action bean helloOffspringAction and command object helloOffspringCommand.
com.offspring.examples.helloworld.HelloOffspringValidator class.
mode parameter values greeting and form to the views helloOffspringForm and helloOffspringGreeting.
Two maps, viewSuccessMap and viewFailureMap, are used to map mode parameter values to the view name. In this particular example, let's assume that the user did not provide his/her name in
the entry form. The JSP page will set the mode parameter to the value greeting. Routine validation will fail the execution. As a result, the viewFailureMap property's <prop key="greeting"<helloOffspringForm>/prop> takes effect, and the generic controller renders the view helloOffspringForm repeatedly, until the user submits his/her name. We should not forget to modify bean urlMapping. The line <prop key="/hellooffspring">helloOffspringController</prop> will map the hellooffspring path to helloOffspringController. The web.xml file should also be modified to map our path to the Spring dispatcher.
Downloading the war file, deploying it in the Web server environment (Tomcat 5 is a good choice), and typing URL http://localhost:8080/offspring/hellooffspring?mode=form will kick off the Greeting Enterprise Solution. Alternatively, it is available on http://www.whowillbethere.com/hellooffspring. The site whowillbethere.com was entirely built as a proof of the Offspring concept. I have deployed the Greeting Enterprise Solution there as a tutorial aid.
Now, what if want to redirect execution through the user browser? The generic controller provides a fairly simple way to do that, as illustrated in the following examples.
I have built a simple action class JspAction:
public class JspAction extends BaseAction {
public JspAction(){
}
public boolean performAction() {
HttpCommand command =(HttpCommand)getCommand();
String viewName=command.getRequest().getParameter("view");
setView(viewName);
return true;
}
}
I plug it in jspViewController through the helloworld.xml definition and map jspContoller to the path jspView:
<bean id="urlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/helloworld">helloWorldController</prop>
<prop key="/hellooffspring">helloOffspringController</prop>
<prop key="/jspview">jspViewController</prop>
</props>
</property>
</bean>
<bean id="jspViewController" class="com.offspring.frame.GenericController" lazy-init="true">
<constructor-arg>
<bean class="com.offspring.frame.HttpCommand"/>
</constructor-arg>
<constructor-arg>
<value>jspAction</value>
</constructor-arg>
<property name="viewSuccessMap">
<props>
<prop key="menutest">menutest</prop>
</props>
</property>
</bean>
<bean id="jspAction" class="com.offspring.frame.JspAction" />
I modify my web.xml file to map jspView to the Spring Dispatcher servlet:
<servlet-mapping>
<servlet-name>Dispatcher</servlet-name>
<url-pattern>/jspview</url-pattern>
</servlet-mapping>
Now we are armed with a universal way of viewing the arbitrary JSP page through the Offspring execution flow. Just say "/jspView?view=anyJspPage" and magic happens (no clicking heals and Kansas residency required).
Finally, let's build the familiar helloPlain.jsp page. This time, name and visits are resolved from the HTTP request parameters:
<h1>Hello World trough the plain JSP!</h1>
<h2>Regards, ${param.name}</h2>
<p>
Note:<br>
${param.name}, You have met the world ${param.visits} time(s).
Let's test it with the following URI on a local machine: http://localhost:8080/offspring/jspview?view=helloPlain&name=Dorothy&visits=1 or http://www.whowillbethere.com/jspview?view=helloPlain&name=Dorothy&visits=1.
It works fine; therefore, we can change the execution flow of our Greeting Enterprise Solution and redirect through the user
browser to the helloPlain.jsp page. This simple operation requires a slight modification to the helloOffspringController bean via the helloworld.xml file:
<property name="viewSuccessMap">
<props>
<prop key="form">helloOffspringForm</prop>
<prop key="greeting">redirect:jspview?view=helloPlain?name?visits</prop>
</props>
</property>
This modification maps the success view to the value redirect:jspview?view=helloOffspringGreetingPlain?name?visits. The view name starts with the redirect: prefix; therefore, Offspring redirects through the user browser and assigns HTTP request parameters name and visits to the values from the model. If we want to redirect to the other server, we would use in the mapping the view name value
redirect:http://whowillbethere.com/jspview?view=helloPlain?name?visits.
What if helloPlain.jsp was designed to deal with parameters namePlain and visitPlain, instead of name and visit? Another notation resolves this problem. The mapping redirect:jspview?view=helloPlain?namePlain=${name}?visitsPlain=${visits} will dereference values name and visits from the model map and assign them to request parameters namePlain and visitsPlain.
I introduced executors as reusable modules that we could plug into the controllers to modify task logic and eliminate cross-cutting concerns. For the sake of demonstration and to offer a simple tracking example, let's assume we must track some tasks' activities. Here is the simple plan:
BaseExecutor's tracking bean in the Spring XML definition
Follow your own advice
The TrackingManager advice class is an AOP advice that logs out the action activity. We track only successful execution after the succeedAction invocation:
public class TrackingManagerAdvice implements AfterReturningAdvice {
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable {
if(method.getName().equals("succeedAction")){
Action action=(Action)args[0];
trackExecution(action);
}
}
private void trackExecution(Action action) throws Exception{
Log log=LogFactory.getLog(this.getClass());
log.info("***** Tracking***");
log.info("Action="+ action.getClass().getName());
log.info("ActionContext="+action.getActionContext());
log.info("Action:");
dumpBean(action.getCommand());
log.info("Model:");
dumpModel(action.getModel());
log.info("***** EndTracking***");
}
}
The call action.getActionContext() extracts the intermediate result values that Action did not put in the model, but nevertheless are important to track.
Create tracking bean executor and apply AOP via the XML definitions
The helloworld.xml file contains new definitions:
<bean id="executorTrackingTarget" class="com.offspring.frame.BaseExecutor" />
<bean id="executorTracking" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>com.offspring.frame.Executor</value>
</property>
<property name="interceptorNames">
<list>
<value>trackingAdvice</value>
</list>
</property>
<property name="target">
<ref bean="executorTrackingTarget"/>
</property>
</bean>
<bean id="trackingAdvice" class="com.offspring.objects.TrackingManagerAdvice"></bean>
According to the definition above, trackingAdvice will intercept all calls to the Executor interface methods of the executorTrackingTarget object.
Plug in the tracking bean as an executor in the controller
When a task requires tracking functionality, we must plug in the reusable tracking executor in the corresponding generic controller.
One extra line in the controller XML definition adds all desirable functionality:
<bean id="helloOffspringController" class="com.offspring.frame.GenericController">
...
<property name="executors">
<list>
<ref bean="executorTracking"/>
</list>
</property>
...
</bean>
Interestingly enough, we did not have to use AOP for the tracking-executor implementation. The essence of the executor is
to proxy calls from the generic controller to the action, which means we can build executorTracking just by extending the BaseExecutor class:
public class SimpleTrackingExecutor extends BaseExecutor {
public void succeedAction(Action action, Iterator it) throws Exception {
super.succeedAction(action, it);
Log log=LogFactory.getLog(this.getClass());
log.info("***** Simple Tracking (no AOP)***");
log.info("Action="+ action.getClass().getName());
log.info("ActionContext="+action.getActionContext());
log.info("Action:");
dumpBean(action.getCommand());
log.info("Model:");
dumpModel(action.getModel());
log.info("***** EndTracking***");
}
...
}
The XML definition is even simpler:
<bean id="executorSimpleTracking" class="com.offspring.objects.SimpleTrackingExecutor" />
<bean id="helloOffspringController" class="com.offspring.frame.GenericController">
…
<property name="executors">
<list>
<ref bean="executorSimpleTracking"/>
</list>
</property>
…
</bean>
Offspring transaction management provides a compelling example of how we can benefit from plugging specific executors in the generic controller. Transaction management is one of the most challenging tasks we may face in developing the application's persistence layer. To secure critical financial transactions, we must set a high database isolation level. However, applying a high database isolation level for less critical, but frequent operations could cause concurrency problems such as lock escalations, deadlocks, or timeouts. In the e-commerce world, usually, when one user browses available sport cards, other concurrent users are prevented from paying for a subscription because both tasks have the same high-isolation level and block each other's concurrent database access. The chain-of-executor concept gives us technical means for how to handle the problem gracefully.
Using rich Spring transaction-support functionality, we define datasource and transaction executor beans in the application context helloworld.xml:
<bean id="dataSource"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>java:comp/env/jdbc/dbhelloworld</value>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource"><ref bean="dataSource"/></property>
</bean>
<bean id="executorTransTarget" class="com.offspring.frame.BaseExecutor" />
<bean id="executorTrans" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="proxyInterfaces">
<list>
<value>com.offspring.frame.Executor</value>
</list>
</property>
<property name="target">
<ref bean="executorTransTarget"/>
</property>
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
<property name="transactionAttributeSource">
<ref bean="attributeSource"/>
</property>
</bean>
<bean id="attributeSource"
class="org.springframework.transaction.interceptor.MatchAlwaysTransactionAttributeSource">
<property name="transactionAttribute">
<ref bean="myTransactionAttribute"/>
</property>
</bean>
<bean id="myTransactionAttribute" class="org.springframework.transaction.interceptor.DefaultTransactionAttribute">
<property name="propagationBehaviorName">
<value>PROPAGATION_REQUIRED</value>
</property>
<property name="isolationLevelName">
<value>ISOLATION_READ_UNCOMMITTED</value>
</property>
</bean>
The complexity of the executorTrans bean definition does not affect the one-line effort to plug it into the generic controller's chain of executors. Assuming
that instead of the sessions, we are using the MySQL database to log our visitors, the definition of the helloOffspringController will have an extra executor:
<bean id="helloOffspringController" class="com.offspring.frame.GenericController">
...
<list>
<ref bean="executorTracking"/>
<ref bean="executorTrans"/>
</list>
...
</bean>
rom now on, we can use the very same executorTrans bean to support low-isolation-level transactions for all tasks by plugging it into the corresponding chain of the executors.
If we need high-isolation-level transactions for some critical tasks, we would define and plug in another transaction bean
with the isolationLevelName attribute equal to ISOLATION_REPEATABLE_READ. This approach is so flexible that we can even switch from one mode to another by changing one line of the controller definition
in the application context.
Let's look at another common functionality where Offspring delivers a simple and reliable solution: when synchronous response is not required, some tasks should be scheduled. When I implement email notifications, I store email body and attributes in the database table and let the scheduler send bunches of emails every few minutes asynchronously. This way, my application stays up even when the SMPT server is down.
Let's build a scheduler generic controller—the generic and simple way of running the cron jobs. It's designed to kick off actions through OpenSymphony's Quartz scheduler. The code resembles that of generic controller:
public class SchGenericController extends CronTriggerBean implements ApplicationContextAware {
ApplicationContext _ApplicationContext;
String _ActionName = null;
String _CommandName = null;
Object _Command = null;
ArrayList _Executors;
public SchGenericController() throws Exception {
super();
_Executors = new ArrayList();
_Executors.add(new BaseExecutor());
MethodInvokingJobDetailFactoryBean jobDetail = new MethodInvokingJobDetailFactoryBean();
jobDetail.setTargetObject(this);
jobDetail.setTargetMethod("handle");
jobDetail.setBeanName("action");
jobDetail.afterPropertiesSet();
setJobDetail((JobDetail) jobDetail.getObject());
}
public SchGenericController(Object command, String actionName)
throws Exception {
this();
_ActionName = actionName;
_Command = command;
}
public SchGenericController(String actionName) throws Exception {
this();
_ActionName = actionName;
_Command = new BaseCommand();
}
public void setApplicationContext(ApplicationContext context){
_ApplicationContext= context;
}
public void setExecutor(Executor executor) {
_Executors.add(executor);
}
public void setExecutors(ArrayList executors) {
_Executors.addAll(executors);
}
public void handle() throws Exception {
Object command = _Command;
Action action = (Action) _ApplicationContext.getBean(_ActionName);
if(_CommandName!=null){
command = _ApplicationContext.getBean(_CommandName);
//load new instance of the command from XML declaration;
}
action.setCommand(command);
Executor firstExecutor = (Executor) _Executors.get(0);
if (!firstExecutor.initAction(action, _Executors.iterator())) {
firstExecutor.failedAction(action, _Executors.iterator());
} else if (!firstExecutor.performAction(action, _Executors.iterator())) {
firstExecutor.failedAction(action, _Executors.iterator());
} else {
firstExecutor.succeedAction(action, _Executors.iterator());
}
firstExecutor.endAction(action, _Executors.iterator());
}
public void setCommandName(String commandName) {
_CommandName = commandName;
}
}
The scheduler controller is built on the same principles as the generic controller. We can reuse or create new action and
command beans and plug them into the scheduler controller. The only difference is that the SchGenericController class extends Quartz's CronTriggerBean class and triggers handle() method invocations according to the standard Quartz property cronExpression.
This approach provides the simplest possible setup for scheduled processes. It's worth mentioning that the scheduler controller
implements the ApplicationContextAware interface. It allows easy access to the application context for loading command and action beans from Spring XML definitions.
The following fragment from helloworld.xml demonstrates how to set an execution of the CheckWorld action every 10 seconds:
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="testSchedController" />
<!-- Other scheduled processes could be specified here -->
</list>
</property>
</bean>
<bean id="testSchedController"
class="com.offspring.frame.SchGenericController">
<constructor-arg>
<value>testAction</value>
</constructor-arg>
<property name="commandName">
<value>testCommand</value>
</property>
<property name="cronExpression">
<value>0,10,20,30,40,50 * * * * ?</value>
</property>
<property name="executors">
<list>
<ref bean="executorTracking"/>
</list>
</property>
</bean>
<bean id="testAction" class="com.offspring.examples.helloworld.CheckWorld" />
<bean id="testCommand" class="com.offspring.examples.helloworld.HelloOffspringCommand" singleton="false">
<property name="name">
<value>Anonymous</value>
</property>
</bean>
After deployment, this example will produce a simple log message every 10 seconds: Dear Anonymous, the world is still there for you.
We have two perfectly working actions: CheckWorldAction and helloOffspringAction. We use them to check the world's existence and greet the world. It seems a reasonable idea to chain them in the scope of
the same user request, because one does not make sense without the other.
Command chaining is one of the most useful and natural extensions of any framework. The real challenge is doing it without
changing the actions' code. The Composite pattern gives an easy answer: class ChainActions implements interface Action and contains the list of other actions executed in sequential order (similar to the chain of executors). This pattern allows
us to treat a group of actions as a single action.
As we discussed, in the Spring Web layer, actions deal with commands, and commands are populated by Spring from the user request.
In the chaining scenario, however, the second action in the chain would be populated from the model and action context. The
names of corresponding properties from the model and action context could differ from the properties of the command bean.
The interface ChainedCommand addresses the problem:
public interface ChainedCommand {
public void PopulateCommand(Map model, Map actionContext) throws Exception;
public Object getCommand();
}
It provides the wrapper around the command object. The PopulateCommand() method sets the command properties to the corresponding values extracted from the model. Obviously, we do not want to write
the specific implementation of the ChainedCommand interface for each command bean used in the actions chain. The class MappedChainCommand comes in handy as a universal solution. We just have to set the command and modelMap properties to use it. The modelMap property is a string that defines matches between properties of the command object and values in the model map. It resembles
the redirection mapping described above. For example, the notation visitorName=${name} would instruct the MappedChainCommand class to set the visitorName property of the command object to the value of the name object extracted from the model map.
Here is the ChainActions implementation:
public class ChainActions extends BaseAction {
List _Actions;
List _ChainedCommands;
public ChainActions(){
super();
_Model=new HashMap();
}
public void setActions(List actions){
_Actions = actions;
}
public boolean initAction() throws Exception {
Iterator it = _Actions.iterator();
while(it.hasNext()){
Action action= (Action) it.next();
if(!action.initAction()){
return false;
}
addSubmodel(action.getModel(),action.getModelName());
}
return true;
}
public boolean performAction() throws Exception {
Iterator it = _Actions.iterator();
int count=0;
while(it.hasNext()){
Action action= (Action) it.next();
if(count>0){
//populate command from model;
ChainedCommand chainedCommand=(ChainedCommand)_ChainedCommands.get(count-1);
chainedCommand.PopulateCommand((Map)_Model, getActionContext() );
}
if(!action.performAction()){
return false;
}
addSubmodel(action.getModel(),action.getModelName());
count++;
}
return true;
}
...
}
The ChainActions contains the list of the chained commands, except for the first command bean, which is populated by the Spring Web layer.
It assigns commands to the corresponding actions and takes care of synchronizing the model and populating commands properties
by calling the PopulateCommand method.
Application context helloworld.xml wires the structure together:
...
<bean id="urlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
...
<!--<prop key="/hellooffspring">helloOffspringController</prop> -->
<prop key="/hellooffspring">helloOffspringChainController</prop>
</props>
</property>
</bean>
...
<!-- Chaining the Actions -->
<bean id="helloOffspringChainController" class="com.offspring.frame.GenericController">
<constructor-arg><ref bean="helloOffspringCommand"/></constructor-arg>
<constructor-arg><value>helloOffspringChainAction</value></constructor-arg>
<property name="validator">
<bean class="com.offspring.examples.helloworld.HelloOffspringValidator" />
</property>
<property name="viewSuccessMap">
<props>
<prop key="form">helloOffspringForm</prop>
<prop key="greeting">helloOffspringGreeting</prop>
</props>
</property>
<property name="viewFailureMap">
<props>
<prop key="greeting">helloOffspringForm</prop>
</props>
</property>
<property name="executors">
<list>
<ref bean="executorTracking"/>
</list>
</property>
</bean>
<bean id="helloOffspringChainAction"
class="com.offspring.frame.ChainActions" singleton="false">
<property name="actions">
<list>
<ref bean="helloOffspringAction"/>
<ref bean="checkWorldAction"/>
</list>
</property>
<property name="chainedCommands">
<list>
<bean class="com.offspring.frame.MappedChainCommand">
<constructor-arg>
<ref bean="checkWorldCommand"/>
</constructor-arg>
<property name="modelMap">
<value>visitorName=${name}?reference=test</value>
</property>
</bean>
</list>
</property>
</bean>
The helloOffspringChainController controller is identical to helloOffspringController, except it uses the helloOffspringChainAction action, instead of helloOffspringAction. We have also changed the urlMapping to point to the chain controller.
Deployment and running this example through the URL http://localhost:8080/offspring/hellooffspring?mode=form will launch the
familiar Greeting Enterprise Solution with additional records in the log supplied by checkWorldAction.
In the course of this article, we have managed to build a simple Web application solution based on the Spring framework. The generic controller and chain of executors decouples the Web application from the Spring Web layer, simplifies design and development of Web applications, and provides a flexible framework for the most common requirements, such as scheduling, tracking, transaction management, redirection, and command chaining. The Offspring principles are not limited to the Model-View-Controller modules and could be employed at the much deeper level of business services, managers, and Web services.
Special thanks to my friends and colleagues Tom Griffin and Eric Van Stegeren for their patient support and valuable contributions to this article.
Read more about Enterprise Java in JavaWorld's Enterprise Java section.
Archived Discussions (Read only)