|
JGATMS is a web-based application built on the J2EE Platform. The
client tier can be any web browser that supports standard HTML.
As a J2EE web-application it adheres to the Model-View-Controller
design pattern. Therefore, support for other clients can be added
by introducing a new non-HTML View layer. Several open source
Frameworks were used to implement the architecture. They include:
-
Struts.
An
implementation of the MVC design pattern produced by the Jakarta
project of the Apache Software Foundation.
-
Commons-Chain.
An implementation of the Chain of Responsibility design pattern also
produced by the Jakarta project. Note: At the time of this
writing commons-chain is still a "sandbox" component. This means
that it is not really production-ready. No modifications were
made to commons-chain to use it in this application.
-
Hibernate.
An
object-relational mapping tool supported by the JBoss Group.
The architecture of JGATMS is designed around a command pattern.
This means that each function of the application is a command or series
of commands executed in a chain. The set of commands available
are stored in a catalog and accessible by name. The web-tier of
the application is a set of Struts Action classes that execute one or
more command chains. The application can be customized by adding
commands to each chain and adding actions to execute new chains.
Existing commands and actions can also be modified if needed.
The
JgatmsContext
class is an implementation of the context
that provides easy access to JGATMS-specific attributes.
As stated earlier, JGATMS commands are built on the still experimental
Jakarta Commons-Chain component. Commons-Chain provides an API to
build applications as very small pluggable units of funtionality.
Components can be implemented as
Commands
or
Filters
. Commands
are very simple Java classes that adhere to the following API:
public interface Command {
public boolean execute(Context context) throws Exception;
}
Complex functions are built by configuring chains containing an
arbitrary number of command implementations. JGATMS has
implemented a base command class that contains several helper
APIs. This class is
inklings.jgatms.command.BaseCommand.
Commands in the application should extend this class to
take advantage of the helper methods and ensure that they live well
within the application.
Commands have several semantics that must be understood to properly
implement them:
-
Commands must encapsulate autonomous units of work.
When writing a
command be aware of whether you are doing too much. A command should do a
single thing - typically a small thing. Even if a command has dependencies
on another thing happening, those dependencies should be represented via
the Context, not as inline code.
-
Commands must be written to live within a chain.
Related to the last
point, multiple commands will be chained together. Commands should be written
such that they get dependent data from Context and set resulting data
in Context.
-
Commands store data in the context object.
Use "get" methods to keep track of the context keys for a command.
This is
a technique suggested in the commons-chain documentation. It clarifies
what sorts of information the command will need access to. For example, if
your command will need a Member value object, instead of calling
context.get("member"), implement a getMemberKey() method on your command
class and call context.get(getMemberKey()). Using this technique, people who
look at the code for your command will be clear about what the command's
dependencies are. Be sure that
you don't rely directly on other commands, but use
them indirectly via the Context. Simple value object sharing through the
Context should be sufficient for most circumstances.
-
Command chains will execute until one of the commands returns
true.
To indicate that the chain should exit without completing,
return true from a command. In nominal cases commands should return false.
This is somewhat confusing at first since it feels more natural to return
true in nominal cases.
-
Use the "dispatch" context attribute appropriately.
JGATMS uses a
"dispatch" key in Context to indicate the general outcome of a command
chain. The ContextKeys class provides three keys that are generally
useful: SUCCESS_DISPATCH, ERROR_DISPATCH, and FATAL_DISPATCH. The
SUCCESS_DISPATCH is the nominal case and will be assumed if no other
dispatch key is given. The ERROR_DISPATCH key is used to indicate that
a correctible error has occured - such as a user input error. Using this
dispatch will generally have the effect of returning the user to the previous
screen to fix the error. The FATAL_DISPATCH key is used to indicate
that a non-correctilbe error has occured, such as a database connection
issue. Using this key will typically display an error page with
instructions to contact a system administrator. In general, a command should
not use one of these keys until an outcome has been determined. For example,
you would probably only set the ERROR_DISPATCH or FATAL_DISPATCH if you
are about to return false f
rom a command because an error has been
encountered. You should be very careful when assuming success by setting
the SUCCESS_DISPATCH key because someone may reconfigure the command
chain and place yours in a different place. You should also avoid
relying on the dispatch key to be set a certain way before doing something.
This would introduce subtle dependencies that might be quite difficult
to debug. Remember, success will be
automatically assumed if the chain completes and another command
has not set a dispatch key. Ultimately, the value of the dispatch key will be used by the
Action classes to determine where control should be dispatched
after execution of the chain. So you can customize the application by
setting any arbitrary dispatch key. It will automatically work
as long as your key is configured as a forward definition in the struts-config.xml file.
There are examples of setting the dispatch key in existing code, but
in general you would do it like this:
context.setDispatch(ContextKeys.ERROR_DISPATCH);
|