-
Separating Business Logic
from the MVC
-
Calling ViewProcessors
From an Action
-
Using a DispatchAction
With MultiViewProcessors
The idea behind using an api like Spine is to
ensure that all layers of application infrastructure are cleanly
separated from each other.
To effectively use spine all business logic must be
written in the appropriate
BusinessDelegate with the calling
class performing the role of a validator, front controller,DTO
packager and result interpreter for the business process.
This is done to ensure that the application which is been
created can be easily adapted to use any other MVC system
without needing to make any changes to the business or data tier
of the application. It should also be possible to easily use
differing view systems to access the same application which
obeys this principles.
Client developers are advised that the
ViewProcessor is not part of the
business tier but acts as a connector between the business tier
and the front controller.
To call a
ViewProcessor from within a
struts action, the client developer need only pass the alias of
the
ViewProcessor as defined in the
configuration file to the
ViewProcessorFactory. A
ViewProcessor is obtained and its processData method is called
to obtain a
ResultObject will then be
obtained which contains:
-
The returned object created by the invocation
-
A flag indicating success or failure of the invocation
-
Success and/or error messages detailing the invocation process
To comply with the architectural definition, the StrutsAction
must perform validation and instantiate other objects which may
be needed by the invocation call to the
ViewProcessor. These may include
the DataSource,the
DTO,the
DAOInput object used to
initialize the
DAO and any other parameters that
are needed by the invocation call.
If using a custom
ViewProcessor, you may override
its processNavigation method to send the StrutsAction a flag
inidicating where the StrutsAction should
forward the user, otherwise navigation may be
handled directly in the StrutsAction.
Messages which are obtained from the
ResultObject should also be sent
to the user via the Struts message handling features or a custom
taglib can be written to handle the errors directly.
An example is shown below:
public
class ActivationAction extends Action{
public ActionForward execute(final ActionMapping mapping,
final ActionForm form,
final HttpServletRequest request,
final HttpServletResponse
response) throws Exception {
Common.setLocale(request);
Locale locale = Common.getLocale(request);
ActionForward forward = null;
final ActionErrors errors = new
ActionErrors();
final ActionMessages messages = new
ActionMessages();
final String activationCode =
request.getParameter("actc");
final String userId =
request.getParameter("uvc");
if ((userId != null) &&
(activationCode != null)) {
// create a
DataTransferObject
final StringAttributeBean
lvb = new StringAttributeBean(userId,activationCode);
final ResultObject
resultObject = runProcessor("Activation",lvb,
null,-1);
// process the result
object
if
(!resultObject.isErrorFlag()) {
final
Identifier id = (Identifier) resultObject.getObj();
messages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(
"activation.success.msg", id.getFirstName(), id
.getUserName(), id.getAccountSecrets()
.getPassword1()));
} else {
String s =
resultObject.getDisplayMessages(locale).getAllErrorString();
log.debug(s);
errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(
"activation.error"));
}
} else {
errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(
"activation.null.code"));
}
if (errors.isEmpty()) {
forward =
mapping.findForward("activation.success");
request.setAttribute("messageTitle",
"Your Registration Has Been
Verified!!");
if (!messages.isEmpty())
{
saveMessages(request, messages);
}
} else {
forward =
mapping.findForward("activation.failure");
request.setAttribute("messageTitle",
"Registration Failure!!");
saveMessages(request,
errors);
}
return forward;
}
/**
* Runs the process from view to data layer, accepts null as the
DAOInput value
*
* @param processorName The name/key of the processor we are
using
* @param dto The dto which will be used by the system while
processing this request
* @param request The http request in use by this invocation
* @param key The key by which the datasource is known in
configuration
* @param daoConstructor The constructor object used by the data
access object for this process
* @return A result object containing the result of this
invocation
*/
public ResultObject runProcessor(String processorName,
DataTransferObject dto, DAOInput daoConstructor,int operation)
{
DataSource dataSource =
DataSourceServiceLocator.getDataSource(); return
getProcessor(processorName).processData(dto, dataSource, daoConstructor,operation);
}
/**
* Invokes the named view processor and returns a copy for
further use
*
* @param processorName
*
The name of the processor to invoke
*/
private ViewProcessor getProcessor(final String processorName)
{
return
ViewProcessorFactory.getInstance().createProcessor(
processorName);
}
}
All the rules above apply to
DispatchActions but Spine goes a step further to provide a means
of declaring the main object been processed via configuration,
it also makes it possible to use one
BusinessDelegate to service all
the methods of the DispatchAction whilst providing a Builder
pattern (object) which can be used to
instantiate,passivate,service or delegate methods needed by the
managedobjects used in this configuration.
This makes it posible to use a
MultiViewProcessor with a
DispatchAction as compared to using several
ViewProcessors with one Struts
DispatchAction. A typical
MultiViewProcessor configuration
is as shown below:
<processor name="MultiViewProcessor">
<processorClass>com.zphinx.spine.unittests.impl.processors.MultiViewTestProcessor
</processorClass>
<multiDelegate>
<subclass>
com.zphinx.spine.unittests.impl.delegates.MultiViewTestDelegate</subclass>
<managedObject>
<objectClass>com.zphinx.spine.examples.beans.SpineBean1</objectClass>
<dataAccessObject>
<className>com.zphinx.spine.examples.dao.SpineBean1DAO</className>
<proxyIndex>1</proxyIndex>
</dataAccessObject>
<pageIndex>1</pageIndex>
</managedObject>
<managedObject>
<objectClass>com.zphinx.spine.examples.beans.SpineBean2</objectClass>
<dataAccessObject>
<className>com.zphinx.spine.examples.dao.SpineBean2DAO</className>
<proxyIndex>2</proxyIndex>
</dataAccessObject>
<pageIndex>2</pageIndex>
</managedObject>
<managedObject>
<objectClass>com.zphinx.spine.examples.beans.SpineBean3</objectClass>
<dataAccessObject>
<className> com.zphinx.spine.examples.dao.SpineBean3DAO</className>
<proxyIndex>2</proxyIndex>
</dataAccessObject>
<pageIndex>3</pageIndex>
</managedObject>
<builder>com.zphinx.spine.examples.processors.SomeBuilder</builder>
</multiDelegate>
</processor>
To use a configuration like that shown above, the DispatchAction
or any of its affiliated objects will instantiate the
ViewProcessor from the
ViewProcessorFactory, the
developer can then create a
SpineBean from a builder
specified in configuration and subsequently invoke the process
flow. eg:
try{
DataSource dataSource = DataSourceServiceLocator.getDataSource();
MultiViewProcessor
mvp = ViewProcessorFactory.getInstance().createProcessor(
processorClass);
SomeBuilder builder =
mvp.findBuilder("multiViewProcessor","MultiViewTestDelegate");
//create a bean for your use
SpineBean2 sp2 = builder.createBean(2); //popuate
the bean's methods
sp2.setModifiedDate(new Date()); //to invoke a
process and run methods in the business delegate
ResultObject
resultObject = mvp.processData(sp2,
dataSource,null,4,-1);
//process
the ResultObject as in the above example } catch(Exception
e){ e.printStackTrace(); }
It can be seen from the example above that passing a
pageNumber/pageIndex to the
MultiViewProcessor will
indicate to the
MultiViewProcessor which object
is been processed and which
DataAccessObject should be
invoked for the particular process flow.
User should note that in certain instances (eg invoking the
builder) there is a need to pass the name of the
BusinessDelegate in use to the
stated method. This is because a
MultiViewProcessor is capable of
using more than one
BusinessDelegate to service it's
request.
|
|