In the context of Axis2 architecture, a flow is a collection of phases. The Axis2 engine has two flows, namely the In-flow and the Out-flow. Based on the message invocation pattern in use, the engine will invoke these flows. Obviously, as the names suggest, in-flow is invoked when the engine receives a message and the out-flow is invoked when the engine sends a message.
The real implementation of the engine does not contain an object or struct named flow; rather a set of phases put together in a one-dimensional container represents the concept of a flow. The order in which the phases should be put together to form a flow can be configured in axis2.xml configuration.
A phase is a collection of handlers. The Axis2 engine invokes the phases in the order in which they are placed in a flow. Each phase in turn invokes the set of handlers in it, again in the order in which the handlers are ordered in the phase.
Axis2 architecture has a set of pre-defined phases. They too are configured in the axis2.xml configuration file. For the in-flow, the pre-defined phases are:
- Post Dispatch
For the out-flow, the only pre-defined phase is:
These phases are defined within the phaseOrder elements in the axis2.xml configuration file, in the order in which they should be placed in the flow. The phaseOrder element's type attribute identifies the flow for which the phase order is defined.
The following diagram illustrates how the phases in the above sample are ordered in the respective flows.
Figure 1: Phases in Flows
In addition to the pre-defined phases, a user can add user-defined phases to the flows in the configuration file. For the in-flow, user-defined phases can be added only after the PostDispatch pre-defined phase. This is because the Axis2 engine keeps the flow related information attached to the operations, and it is only after the Dispatch phase that the operation will be found. There are no rules restricting the placement of user-defined phases in the out-flow. If any of the user-defined phases has the same name as that of a pre-defined phase, the engine will fail to operate.
In the above example, two user-defined phases are added to both the in-flow and the out-flow, in addition to the pre-defined phases. Defining a new phase does not involve any programming as far as the user is concerned.
A handler is the smallest unit of invocation in the Axis2 engine. When a phase is invoked, each handler in the chain of handlers within the phase will be invoked. Depending on the flow on which the phase containing the handler is placed, the handler could choose to do any form of processing. The most common use case for handlers is to act as a unit of processing for a module that deals with the quality of service aspects of a Web service.
The best example of a pair of handlers can be found with the WS-Addressing implementation. The "AddressingInHandler" is placed in the PreDispatch phase of the in-flow. "AddressingInHandler" deals with the processing of incoming addressing headers in the request. The "AddressingOutHandler" handler is placed in the MessageOut phase of the out-flow, and prepares the outgoing addressing headers in the response message. Axis2/C comes with built in WS-Addressing, and the handlers are packaged and configured to deal with the addressing headers.
The rule of thumb is to deal with SOAP header processing within a handler. This is because the SOAP body's payload will be dealt with business logic processing. Business logic is the responsibility of either the message receiver on the server side or payload processing logic on the client side. However, there can be situations where a handler needs to deal with the whole SOAP message. An example is a security handler that needs to decrypt an encrypted incoming message before presenting it to the rest of the engine.
A module is a collection of handlers along with its configuration. Unlike flows, phases, or handlers that make the Axis2 engine work at run-time, a module is a configuration concept. Modules enable the Axis2 user to extend the Axis2 engine's behavior by adding new handlers to the phases as desired.
A module can have one or more handlers. In addition, a module also has a configuration file that defines how the handlers of the module are to be placed into the phases. When configuring a module, users can place the handlers in a module into pre-defined phases of the Axis2 engine.
Adding a new module involves programming, unlike adding a new phase. For all the handlers of the module, the axis2_handler interface needs to be implemented. Basically, this means that the invoke method with the following signature needs to be implemented by each handler:
axis2_status_t (AXIS2_CALL *
const axis2_env_t *env,
struct axis2_msg_ctx *msg_ctx);
Once the handlers are implemented, the module needs to implement two API calls to facilitate dynamic loading. These functions are:
int axis2_get_instance(axis2_module_t **inst, const axis2_env_t *env);
int axis2_remove_instance(axis2_module_t *inst, const axis2_env_t *env);
The Axis2 engine looks for these functions and uses them when loading and unloading the module.
Once the source code for handlers and module wrapping code with the above functions are ready, they can be compiled into a shared library. In order to use the module, a configuration file is also required. The following shows a sample configuration file for the WS-Addressing that comes with Axis2/C
<module name="addressing" class="axis2_mod_addr">
<handler name="AddressingInHandler" class="axis2_mod_addr">
<handler name="AddressingOutHandler" class="axis2_mod_addr">
In the above configuration file, the name of the module is given as "addressing", along with the name of the shared library implementing the module. It also configures the two handlers in the addressing module, the AddressingInHandler in the PreDispatch phase of the in-flow and the AddressingOutHandler in the MessageOut phase of the out-flow.
Figure 2: Module Handlers into Phases
Deploying a Module
When both the set of handlers and the configuration file are prepared, a module can be deployed. To deploy a module, a folder with the same name as the module name needs to be created in the modules folder of the deployment repository of Axis2/C. (Deployment repository is the folder where the axis2.xml configuration file, services folder and modules folder are placed. For more information on Axis2/C deployment folder, please refer to Axis2/C documentation) Once this folder is created, the shared library and the module.xml configuration file need to be placed in that folder. When the module is placed in the deployment repository, the module is said to be deployed. It is mandatory that a module be deployed before it can be used by the Axis2 engine.
Engaging a Module
After deploying a module, in order to let the Axis2 engine pick that module and insert the handlers in that module into the respective phases configured by the module.xml file, the module needs to be engaged. Even if a module is deployed, but not engaged, the engine will not include the handlers of that module in the execution chain.
A module can be engaged by adding a 'module ref' entry in the configuration file.
The above would engage the addressing module.
A module can be engaged globally by having a module ref entry in the axis2.xml file. Engaging a module globally means that all the services in the system would make use of that module. A module can also be engaged at the service level by having the module ref entry in the services.xml file of a service.
At the start up of the client or server, Axis2 would traverse the repository folder to find the deployed modules in the modules folder. It will look for the module.xml configuration file in each sub folder within the modules folder, and collect information on all the deployed modules. Once the deployed modules are found, it would look for module ref entries when going through the configuration files. If the modules referred to by those entries are in the list of deployed modules, then that module would be engaged depending on the level at which the engaging happened (global, service level, etc.)
Figure 3: Module Life Cycle
Use of Modules
The primary use of modules is to provide WS-* spec support. Web services Addressing, Security, Reliable Messaging, and Eventing are all implemented using modules.
Modules can also be used for purposes other than implementing WS-* support. Axis2/C comes with a logging module example. WSO2 WSAS for Java that is based on Apache Axis2/Java, comes with a throttling module for request policing. Another potential use would be for accounting- a module that accounts for statistics such as total number of requests, faulty requests, successful vs. fault responses, etc.
In a Nutshell
The Axis2 engine consists of flows, in-flow and out-flow, and based on the message exchange pattern in use, all or some of those flows will be invoked. A flow is a collection of phases arranged in a linear container in some configured order. Each phase in a flow will be invoked when the engine invokes a flow. A phase is in turn a collection of handlers, again arranged in a linear container in a configured order. When a phase is invoked, the phase will invoke all the handlers in it, in the order in which they are stored within the phase. Modules enable Axis2 users to extend the Axis2 engine's behavior by introducing new handlers into the phases of the engine. Users can configure a module, with information such as in which phase to place the handlers along with rules on how to place handlers within named phases.
Samisa Abeysinghe, Software Architect, WSO2 Inc. samisa at wso2 dot com