The architecture of the coefficient system has been designed for enterprise level usage. It uses J2EE components so that it can take advantage of built-in replication, fail-over, life cycle and transactional controls. Because the interface is web-based, the system utilizes optimistic locking to further demarcate a transaction that spans a users multi-request interaction.
The system has been designed so that all entities that process an incoming request from the browser are either a self-contained module or a conglomeration of module invocations. Therefore the module is a very important component of the system. Modules can be requested by name from the browser. Modules contain any number of named operations which are implemented as methods on the module. Operations can also be requested by name from the browser. All modules in the system are represented by Stateless Session Beans. This is done so that the EJB container can handle any load-balancing that may be needed.
The system uses a three tiered architecture. The first tier is the web container where we use a single servlet to handle all incoming requests to the system. This allows us to force the activation through an interceptor chain. The interceptor chain allows us to catch all exceptions that may be thrown from the system, perform global configurations, and capture the request and response into a well defined context that becomes the contract with middle tier components. There is a set of objects that exist in the first tier that cross the remote boundary that exists between the web and ejb containers. These objects are used as the communications mechanism between the web world and the EJB world. In the diagram above these objects are the CoefficientContext, Page, Theme, Project, and all objects that the project is composed of. The bridge between this tier and the middle tier is the ExecuteModule Interceptor where a remote call to the module invoke facade is made.
The middle tier of the system is where all the business logic and presentation logic resides. A module is responsible for the presentation of its content, the execution of its business logic, and the persistence of its data. The system contains two different kinds of modules, system modules, and project modules. System modules are core modules that form the backbone of the system. They relate to the working of the system as a whole (user administration, searching, navigation, etc.). Project modules are components that augment the abilities of a project (file upload, discussion forums, votes, tasks, etc.). Data objects are owned and maintained by modules in the system. For example, Project object is a core object to the system that is persisted by the project module. The FileUpload object is contained by the file upload module and this object has an internal reference to the Project object since it is a project module but the reference is unidirectional so module independence is maintained.
A key aspect of the system is that a project can have a workflow attached to it. The workflow can dictate the behavior of the project and defines which modules a project will include. The workflows are defined in XML. The system uses Castor to generate the object model used to represent the data in memory.
The workflow allows a user to define a set of states, state transitions, rules, and actions. States allow a user to define which modules will be visible when in that state. They also allow definition of any number of State Transitions. A State Transition allows a user to define a set of Rules that will cause the workflow to move into a different state. They also allow the user to define any actions that should be taken on moving from the current state. Rules can specify Comparators that allow a user to compare values that can be specified in a hard-coded manner or specified by an Action. Comparators can be any java object that implements a compare method. If the user defines a custom comparator then it must be in the applications classpath and must be specified in the xml. Actions are java class method invocations or module method invocations. They can be used to dynamically retrieve information that can be evaluated or to request that the system does things such as creating Tasks. Actions can be performed on state entry and state exit and can be an element within a rule. If all the rules evaluate to true then a transition will be made, otherwise the workflow will stay in the current state.
The system defines a simpleWorkflow example. This workflow forces a project to first add 2 members to the project. No other modules are visible until this has been completed. Once completed the workflow creates a task for the user to complete and makes the task module viewable. Once completed the workflow make a fileUpload item for the content created by the task. The task module is not viewable but the file upload is. Once a file has been uploaded the workflow creates a vote to see if the upload is acceptable. If 60% of the vote is positive the workflow moves into an open project state where all modules are viewable. If 60% of the vote is negative it moves back to the Task state.
These powerful mechanisms allows a user to create a workflow that will guide the user through stages of content creation without the project user having to know all the domain knowledge that the workflow writer has. The workflow is utilized by the coefficient architecture but it is self-contained and is available as the csir-workflow project(LGPL).
The system needs to keep statistics on how much activity a project is receiving. Every create, update, and completion of project elements needs to contribute to this ranking. Because this is a cross-cutting concern we have used an Aspect oriented approach to solve this problem. We define pointcuts so that any module that implements BaseProjectModule and calls save or update through the HibernateUtilility or on the Hibernate Session object will be intercepted. This interception then fires off a message to a statistics message queue. The message queue has a durable subscriber which is implemented as an EJB Message Bean. This is done so that statistical updates can be done asynchronously. The message bean updates the statistics associated with the project in the persistent storage.
Modules drive the system, but modules can have different roles in the system. Therefore we needed a way to categorize what role a module plays in the system. We use JNDI namespaces to correctly categorize modules. The top level namespace used in the project is za/org/coefficient. There are nine categories that are defined by their own namespaces. They are:
The third tier, the persistence layer, is abstracted through the use of Hibernate. We wrap invokes to hibernate through a utility class, HibernateUtilility, so that we can add some features to the default hibernate configuration. The data objects discussed in the middle tier are persisted through this layer and translated to a relational structure. Hibernate participates in the JTA designated transactions that are demarcated upon entry into a SessionBean module. All system session beans transaction level is set to Required.