SOA, in practicality web services, is becoming the enabler for application integration. Since the introduction of web services, Apache Software Foundation has played a major role and produced several good web services frameworks. This book covers the defector Java Web Service framework, also known as Apache Axis2. This book covers several important facts that you would want to know about web services and writing, from simple web services to complex web services. By the end of this book, you will have learned about Axis2, its architectures and features, writing and deploying a simple service, writing service extensions and quality of services, POJO and JAX-WS services, clusters, and secure reliable web services.
also read:
What This Book Covers
Chapter 1, Apache Web Services and Axis2 – Gives you an introduction to web services and the web service stack at Apache.
Chapter 2, Looking inside Axis2 – Learn about Axis2 architecture and the importance of its components.
Chapter 3, Axis2 XML Model (AXIOM) – Learn about the heart of a web service framework and learning more about XML processing in Axis2.
Chapter 4, Execution Chain – Learn how to extend the core functionality of the framework though handlers.
Chapter 5, Deployment Mode – Learn about the new and user friendly deployment model and several ways of deploying a service in Axis2.
Chapter 6, Information Model – Learn how Axis2 stores it static and dynamic data and the importance of it.
Chapter 7, Writing an Axis2 Service – Learn how to write a simple-complex service using Axis2 and how to deploy it
Chapter 8, Writing an Axis2 Module – Learn how to extend Axis2 core functionality
through a self-contained package.
Chapter 9, The Client API – Learn how to use Axis2 to invoke other services, available APIs, and how to use them.
Chapter 10, Session Management – Go beyond single invocation and learn how to use Axis2 features to provide better and more efficient statefull service.
Chapter 11, Developing JAX-WS Web Services – Learn the fundamentals of developing JAXWS based web services, the most popular web service development technology used by Java developers.
Chapter 12, Axis2 Clustering – Learn about clustering Apache Axis2, which will allow
you to deploy Axis2 in large scale production deployments.
Chapter 13, Enterprise Integration Patterns – Learn about some enterprise SOA deployment patterns that make use of the underlying Axis2 clustering infrastructure.
Chapter 14, Axis2 Advanced Features and Usage – Go beyond simple features and learn about REST, MTOM, and several other advanced features.
Chapter 15, Building a Secure Reliable Web Service – Learn how to use Axis2 and related components to make your service more secure and reliableWriting an Axis2 Module Web services are gaining a lot of popularity in the industry and have become one of the major enabler for application integration. In addition, due to the fl exibility and advantages of using web services, everyone is trying to enable web service support for their applications. As a result, web service frameworks need to support new and more custom requirements. As we have already discussed in the previous chapters, one of the major goals of a web service framework is to deliver incoming messages into the target service. However, just delivering the message to the service is not enough; today’s applications are required to have reliability, security, transaction, and other quality services.
Due to the popularity of web services, standard bodies are producing new web service standards, and it is hard to support those new standards if the web service framework is not fl exible enough. From the very beginnings of Axis2, fl exibility and extensibility were the two main design considerations. The idea of Axis2 modules is to extend the core functionality of the system without performing any changes to the core system.
For example, Axis2 supports reliability and security as two separate modules, and the core engine is fully independent of those two qualities of service modules.
In this chapter, we will discuss the power of Axis2 modules and how to use them to extend Axis2 to support for your own requirements. In particular, we will discuss the following items:
- Brief history of the Axis2 module
- Introducing module concept
- Structure of the module
- Module configuration file (module.xml)
- Optional module implementation class
- Steps to writing a module.xml file
- Deploying and engaging a module
- Brief overview of the WS-Policy and its usage in modules
In our approach, we will be using code sample to help us understand the concepts better.
Brief history of the Axis2 module
Looking back at the history of Apache Web Services, the Handler concept can be considered as one of the most useful and interesting ideas. Due to the importance and fl exibility of the handler concept, Axis2 has also introduced it into its architecture. Notably, there are some major differences in the way you deploy handlers in Axis1 and Axis2. In Axis1, adding a handler requires you to perform global configuration changes and for an end user, this process may become a little complex. In contrast, Axis2 provides an easy way to deploy handlers. Moreover, in Axis2, deploying a handler is similar to deploying a service and does not require global configuration changes.
At the design stage of Axis2, one of the key considerations was to have a mechanism
to extend the core functionality without doing much. One of the main reasons behind the design decision was due to the lesson learned from supporting WS reliable messaging in Axis1. The process of supporting reliable messaging in Axis1 involved a considerable amount of work, and part of the reason behind the complex process was due to the limited extensibility of Axis1 architecture. Therefore, learning from a session in Axis1, Axis2 introduced a very convenient and flexible way of extending the core functionality and providing the quality of services. This particular mechanism is known as the module concept.
Module concept
In Chapter 4, Execution Chain, we introduced and discussed the handler concepts and how to use handlers. As we discussed there, one of the main ideas behind a handler is to intercept the message fl ow and execute specific logic. In Axis2, the concept of a module is to provide a very convenient way of deploying service extension. We can also consider a module as a collection of handlers and required resources to run the handlers (for example, third-party libraries). One can also consider a module as an implementation of a web service standard specification. As an illustration, Apache Sandesha is an implementation of WS-RM specification. Apache Rampart is an implementation of WS-security; likewise, in a general module, is an implementation of a web service specification. One of the most important features and aspects of the Axis2 module is that it provides a very easy way to extend the core functionality and provide better customization of the framework to suit complex business requirements. A simple example would be to write a module to log all the incoming messages or to count the number of messages, if requested.
Module structure
As mentioned a few times before in previous chapters, Axis1 is one of the most popular web service frameworks and it provides very good support for most of the web service standards. However, when it comes to new and complex specifications, there is a significant amount of work we need to do to achieve our goals. The problem becomes further complicated when the work we are going to do involves handlers, configuration, and third-party libraries. To overcome this issue, the Axis2 module concept and its structure can be considered as a good candidate. As we discussed in the deployment section, both Axis2 services and modules can be deployed as archive files. Inside any archive file, we can have configuration files, resources, and the other things that the module author would like to have.
It should be noted here that we have hot deployment and hot update support for the service; in other words, you can add a service when the system is up and running. However, unfortunately, we cannot deploy new modules when the system is running. You can still deploy modules, but Axis2 will not make the changes to the runtime system (we can drop them into the directory but Axis2 will not recognize that), so we will not use hot deployment or hot update. The main reason behind this is that unlike services, modules tend to change the system configurations, so performing system changes at the runtime to an enterprise-level application cannot be considered a good thing at all.
As we discussed earlier, adding a handler into Axis1 involves global configuration changes and, obviously, system restart. In contrast, when it comes to Axis2, we can add handlers using modules without doing any global level changes. There are instances where you need to do global configuration changes, which is a very rare situation and you only need to do so if you are trying to add new phases and change the phase orders. You can change the handler chain at the runtime without downer-starting the system.
As mentioned earlier, changing the handler chain or any global configuration at the runtime cannot be considered a good habit. This is because in a production environment, changing runtime data may affect the whole system. However, at the deployment and testing time this comes in handy.
The structure of a module archive file is almost identical to that of a service archive file, except for the name of the configuration file. We know that for a service archive file to be a valid one, it is required to have a services.xml. In the same way, for a module to be a valid module archive, it has to have a module.xml file inside the META-INF directory of the archive. A typical module archive file will take the structure shown in the following screenshot. We will discuss each of the items in detail and create our own module in this chapter as well.
Module configuration file (module.xml)
As we have already discussed, the module archive file is a self-contained and self-described file. In other words, it has to have all the configuration required to be a valid and useful module. Needless to say, that is the beauty of a self-contained package. The Module configuration file or module.xml file is the configuration file that Axis2 can understand to do the necessary work.
A simple module.xml file has one or more handlers. In contrast, when it comes to complex modules, we can have some other configurations (for example, WS policies, phase rules) in a module.xml. First, let’s look at the available types of configurations in a module.xml. For our analysis, we will use a module.xml of a module that counts all the incoming and outgoing messages. We will be discussing all the important items in detail and provide a brief description for the other items:
- Handlers alone with phase rules
- Parameters
- Description about module
- Module implementation class
- WS-Policy
- End points
Handlers and phase rules
As we discussed, a module is a collection of handlers, so a module could have one or more handlers. Irrespective of the number of handlers in a module, module. xml provides a convenient way to specify handlers. Most importantly, module. xml can be used to provide enough configuration options to add a handler into the system and specify the exact location where the module author would like to see the handler running. In Chapter 4, Execution Chain, we learnt more about phase rules as a mechanism to tell Axis2 to put handlers into a particular location in the execution chain, so now it is time to look at them with an example.
Before learning how to write phase rules and specifying handlers in a module.xml, let’s look at how to write a handler. There are two ways to write a handler in Axis2:
- Implement the org.apache.axis2.engine.Handler interface
- Extend the org.apache.axis2.handlers.AbstractHandler abstract class
In this chapter, we are going to write a simple application to provide a better understanding of the module. Furthermore, to make the sample application easier, we are going to ignore some of the difficulties of the Handler API. In our approach, we will extend the AbstractHandler. When we extend the abstract class, we only need to implement one method called invoke. So the following sample code will illustrate how to implement the invoke method:
public class IncomingCounterHandler extends AbstractHandler implements CounterConstants { public InvocationResponse invoke(MessageContext messageContext) throws AxisFault { //get the counter property from the configuration context ConfigurationContext configurationContext = messageContext. getConfigurationContext(); Integer count = (Integer) configurationContext.getProperty(INCOMING_ MESSAGE_COUNT_KEY); //increment the counter count = Integer.valueOf(count.intValue() + 1 + «»); //set the new count back to the configuration context configurationContext.setProperty(INCOMING_MESSAGE_COUNT_KEY, count); //print it out System.out.println(«The incoming message count is now « + count); return InvocationResponse.CONTINUE; } }
As we can see, the method takes MessageContext as a method parameter and returns InvocationResponse as the response. You can implement the method as follows:
- First get the configurationContext from the messageContext.
- Get the property value specified by the property name.
- Then increase the value by one.
- Next set it back to configurationContext.
- In general, inside the invoke method, as a module author, you have to do all the logic processing, and depending on the result you get, we can decide whether you let AxisEngine continue, suspend, or abort. Depending on your decision, you can return to one of the three following allowed return types:
- InvocationResponse.CONTINUEGive the signal to continue the message
- InvocationResponse.SUSPENDThe message cannot continue as some of the conditions are not satisfied yet, so you need to pause the execution and wait.
- InvocationResponse.ABORTSomething has gone wrong, therefore you need to drop the message
and let the initiator know about it - The message cannot continue as some of the conditions are not satisfied yet,so you need to pause the execution and wait.
- InvocationResponse.ABORT.
- Something has gone wrong, therefore you need to drop the message and let the initiator know about it.
The corresponding CounterConstants class a just a collection of constants and will look as follows:
public interface CounterConstants { String INCOMING_MESSAGE_COUNT_KEY = "incoming-message-count"; String OUTGOING_MESSAGE_COUNT_KEY = "outgoing-message-count"; String COUNT_FILE_NAME_PREFIX = "count_record"; }
As we already mentioned, the sample module we are going to implement is for counting the number of request coming into the system and the number of messages going out from the system. So far, we have only written the incoming message counter and we need to write the outgoing message counter as well, and the implementation of the out message count hander will look like the following:
public class OutgoingCounterHandler extends AbstractHandler implements CounterConstants { public InvocationResponse invoke(MessageContext messageContext) throws AxisFault { //get the counter property from the configuration context ConfigurationContext configurationContext = messageContext. getConfigurationContext(); Integer count = (Integer) configurationContext.getProperty(OUTGOING_ MESSAGE_COUNT_KEY); //increment the counter count = Integer.valueOf(count.intValue() + 1 + «»); //set it back to the configuration configurationContext.setProperty(OUTGOING_MESSAGE_COUNT_KEY, count); //print it out System.out.println(«The outgoing message count is now « + count); return InvocationResponse.CONTINUE; } }
The implementation logic will be exactly the same as the incoming handler processing, except for the property name used in two places.
Module implementation class
When we work with enterprise-level applications, it is obvious that we have to initialize various settings such as database connections, thread pools, reading property, and so on. Therefore, you should have a place to put that logic in your module. We know that handlers run only when a request comes into the system but not at the system initialization time. The module implementation class provides a way to achieve system initialization logic as well as system shutdown time processing. As we mentioned earlier, module implementation class is optional. A very good example of a module that does not have a module implementation class is the Axis2 addressing module. However, to understand the concept clearly in our example application, we will implement a module implementation class, as shown below:
public class CounterModule implements Module, CounterConstants { private static final String COUNTS_COMMENT = "Counts"; private static final String TIMESTAMP_FORMAT = "yyMMddHHmmss"; private static final String FILE_SUFFIX = ".properties"; public void init(ConfigurationContext configurationContext, AxisModule axisModule) throws AxisFault { //initialize our counters System.out.println("inside the init : module"); initCounter(configurationContext, INCOMING_MESSAGE_COUNT_KEY); initCounter(configurationContext, OUTGOING_MESSAGE_COUNT_KEY); } private void initCounter(ConfigurationContext configurationContext, String key) { Integer count = (Integer) configurationContext. getProperty(key); if (count == null) { configurationContext.setProperty(key, Integer. valueOf("0")); } } public void engageNotify(AxisDescription axisDescription) throws AxisFault { System.out.println("inside the engageNotify " + axisDescription); } public boolean canSupportAssertion(Assertion assertion) { //returns whether policy assertions can be supported return false; } public void applyPolicy(Policy policy, AxisDescription axisDescription) throws AxisFault { // Configuure using the passed in policy! } public void shutdown(ConfigurationContext configurationContext) throws AxisFault { //do cleanup - in this case we'll write the values of the counters to a file try { SimpleDateFormat format = new SimpleDateFormat(TIMESTAMP_ FORMAT); File countFile = new File(COUNT_FILE_NAME_PREFIX + format. format(new Date()) + FILE_SUFFIX); if (!countFile.exists()) { countFile.createNewFile(); } Properties props = new Properties(); props.setProperty(INCOMING_MESSAGE_COUNT_KEY, configurationContext.getProperty(INCOMING_MESSAGE_ COUNT_KEY).toString()); props.setProperty(OUTGOING_MESSAGE_COUNT_KEY, configurationContext.getProperty(OUTGOING_MESSAGE_ COUNT_KEY).toString()); //write to a file props.store(new FileOutputStream(countFile), COUNTS_ COMMENT); } catch (IOException e) { //if we have exceptions we'll just print a message and let it go System.out.println("Saving counts failed! Error is " + e.getMessage()); } } }
As we can see, there are a number of methods in the previous module implementation class. However, notably not all of them are in the module interface. The module interface has only the following methods, but here we have some other methods for supporting our counter module-related stuff:
- init
- engageNotify
- applyPolicy
- shutdown
At the system startup time, the init method will be called, and at that time, the module can perform various initialization tasks. In our sample module, we have initialized both in-counter and out-counter.
When we engage this particular module to the whole system, to a service, or to an operation, the engagNotify method will be called. At that time, a module can decide whether the module can allow this engagement or not; say for an example, we try to engage the security module to a service, and at that time, the module finds out that there is a confl ict in the encryption algorithm. In this case, the module will not be able to engage and the module throws an exception and Axis2 will not engage the module. In this example, we will do nothing inside the engageNotify method.
As you might already know, WS-policy is one of the key standards and plays a major role in the web service configuration. When you engage a particular module to a service, the module policy should be applied to the service and should be visible when we view the WSDL of that service. So the applyPolicy method sets the module policy to corresponding services or operations when we engage the module.
In this particular example, we do not have any policy associated with the module, so we do not need to worry about this method as well.
As we discussed in the init method, the method shutdown will be called when the system has to shut down. So if we want to do any kind of processing at that time, we can add this logic into that particular method. In our example, for demonstration purposes, we have added code to store the counter values in a file.
Writing the module.xml file
So far, we have written two handlers and module implementation classes. Now, the only remaining thing to do is to write the module descriptor file. When writing module.xml, we have to use phase rules to specify the location of handlers and we have discussed phase rules before (it is time to refresh our mind about the phase rule). The most simple module.xml file for our module is shown here:
<module name="counterModule" class="org.apache.axis2.sample.module. request.CounterModule"><Description> Counts the incoming and outgoing messages</Description><InFlow><handler name="IncomingMessageCountHandler" class="org.apache.axis2.sample.module.request. IncomingCounterHandler"><order phase="Transport" after="RequestURIBasedDispatcher" before="SOAPActionBasedDispatcher"/></handler></InFlow><OutFlow><handler name="OutgoingMessageCountHandler" class="org.apache.axis2.sample.module.request. OutgoingCounterHandler"><order phase="MessageOut"/></handler></OutFlow></module>
In the file module.xml, we have specified the description of the module as “Counts the incoming and outgoing messages”; in the meantime, it has specified the two handlers we implemented earlier with phase rules.
As you can see, we try to put our incoming message counter into the transport phase and the exact location is after RequestURIBasedDispatcher and before SOAPActionBasedDispatcher. If you look at the default axis2.xml file, you can see the two handlers in the inFlow. Meanwhile, in the outgoing message, a counter is added to the message-out phase and this does not specify the exact location.
If you look carefully, you can see in the root element that there is an attribute called
“class”, which specifies the module interface class. We need to remember that this attribute is an optional one and some modules may or may not have this attribute.
There are instances where the module author needs to use a new phase for the module. In such a situation, one can edit the axis2.xml and specify the new phase(s) one wants. Once specified, phases can be used inside the module.xml.
In the module.xml file, we have not discussed how to add the fault handlers, as we already know, there are two fl ows for fault processing: InFaultFlow and OutFaultFlow. The InFaultFlow is executed when there is an incoming fault. Similarly, OutFaultFlow is executed when there is an outgoing message. If you want to add handler(s) to any of those phases, you can do that just like you did with the other two flows.
Deploying and engaging the module
Now we have written everything you need for a valid module, the only remaining thing is to create the module archive file and deploy it to the repository. First we compile our source code and then, as we know, it creates .class files.
Assuming org.apache.axis2.sample.module.request is the package name of our source file, we can find all the .class files under classes/org/apache/axis2/sample/module/request.
Now create a directory called META-INF under the classes directory and copy the module.xml file into it. Then our classes directory should look like the following screenshot:
Now create a ZIP file from the classes directory and rename the ZIP file into the counter-module.mar.
Deploying the module is as simple as dropping the file into the TOMCAT_HOME/webapps/axis2/WEB-INF/modules or repository/modules directory. In this case, let’s focus on deploying the module in Tomcat or your favorite web application server. As we know, Axis2 does not support module hot deployment, so just dropping the module will not make any changes to the runtime system. To deploy our module and apply the necessary changes to runtime, we need to restart Axis2; in other words, we need to restart the application server.
It should be noted that just deploying a module does not add its handlers into the handler chain; to add handlers into the system, it is necessary to engage the module. Now, let’s see how we engage a module in Axis2. For this purpose, we can use the Axis2 web administration console. Moreover, to make the story simple, let’s try to engage the module to all the services in the system using the web administration console (we can engage to a single service also using the administration console).
We can engage the module to the system by carrying out the following steps:
- Go to http://localhost:8080/axis2/.
- Click on the Administration tab. This will open up a new page and ask for the username and password.
- Enter admin as the username and axis2 as the password. This will open up the administration console.
- After that, click on the Available Modules in the side navigation menu on the left-hand side. There we will find our Counter module as counterModule: Counts the incoming and outgoing messages.
- Now go to Engage Module\For all Services and this will open up a new page with a drop-down menu.
- Select counterModule from that and click engage.
- Now we should see the message counterModule module engaged globally successfully.
- Go to Global Chains and you will be able to see the IncomingMessageCountHandler handler in the transport phase between RequestURIBasedDispatcher and SOAPActionBasedDispatcher.
Now, this simply tells us that we have engaged the module successfully and we have added the handler into the correct phases to invoke the version server, enter the following into your browser:
http://localhost:8080/axis2/services/Version/getVersion
Then, in the console, you will see the following:
This simply tells us that the request has gone though the incoming counter handler and the response has gone though the outgoing counter handler. Now, let’s invoke the service one more time and see what we get in the console. We will see the following:
Now we know how to write a very simple module, deploy it, and engage it. As an exercise, we can change the module.xml and see what happens. Also, we can change the phase rules and see whether it puts the handlers into the correct locations. In the meantime, we can restart Tomcat, without engaging the module. Here, we should not see any output in the console. This will help us to understand that handlers are invoked only when we engage the module.
There are other ways of engaging a module, for example, to engage a module globally, a user can edit axis2.xml and add the <module ref=”modulename”/> element. Moreover, if a user wants to engage it to particular services, he/she can do that by editing the services.xml and adding the same tag mentioned earlier.
Advanced module.xml
Here, we looked at a very simple module.xml, but when it comes to very complex applications, we need to have many more configurations in a module.xml file. We might need to have parameters, WS-Policy, and endpoints. Now let’s look at them one at a time in brief.
Parameters
Adding a parameter here is the same as adding a parameter in services.xml or axis2.xml. We just need to add the following tag into module.xml and Axis2 will do the right thing for us:
<parameter name=”foo”>bar</parameter>
We can have any number of parameters in a module.xml, and when we want to access the parameter, we can do that by carrying out the following steps:
- First we need to get the AxisModule. We can do that either by using the init method (Axis2 passes the AxisModule ) or we need to get the corresponding AxisModule from the ConfigurationContext (inside the Module implementation class) or from messageContext (inside a handler).
- After this, we can ask for the parameter from the AxisModule.
WS-Policy
Specifying a WS-Policy element in module.xml is one way of configuring a module. We can add WS-Policy element inside a module.xml. If we consider the Sandesha2 (reliable message implementation) module, we can find the following policy element:
<wsp:Policy xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/ oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:sandesha2="http://ws.apache.org/sandesha2/ policy" wsu:Id="RMPolicy"><sandesha2:RMAssertion> [REST OF THE FILE]</sandesha2:RMAssertion></wsp:Policy>
Endpoints
In Axis2, an endpoint is an operation of a web service. So, adding an endpoint is the same as adding an operation. The key question is—why do we need to add an endpoint to a module? To understand this, let’s assume that we have a module and this module has a set of control operations. The most suitable example is reliable messaging, as it has a number of control messages (for example, create sequence, last message, terminate sequence). Say, we need to invoke a service in a reliable manner. It first needs to set up a sequence with the service. For this, it will send the control message called createSequence to the service we need to access. But that service does not have that method, so if we try to send the createSequence message without adding the method, Axis2 will throw an exception saying Unable to dispatch. Therefore, adding an endpoint will solve that issue. Hence, when we engage Sandesha, it adds a method called createSequence to the service at the time we engage the module (or all the services, if we engage that to the whole system). When a request comes, Axis2 will dispatch without having any problem. This operation or the endpoint has its own message receiver to do the right thing.
So, it is obvious that when a module needs control operations exchange, it is required to add endpoint to represent those operations. Adding an endpoint is very simple.
What we need to do is add an operation element(s) along with a message receiver and a set of action mapping. To get an idea about that, let’s take the Sandesha module as a reference. Its module.xml has the following operation tag to add the control operations. Remember, when we have the operation tag in module.xml, Axis2 will do all the processing, including creating AxisOperation and adding that. As a module author, all we need to do is specify them in the module.xml file:
<operation name="Sandesha2OperationInOut" mep="http://www. w3.org/2006/01/wsdl/in-out"><messageReceiver class="org.apache.sandesha2.msgreceivers. RMMessageReceiver"/><!-- namespaces for the 2005-02 spec --><actionMapping>http://schemas.xmlsoap.org/ws/2005/02/rm/ CreateSequence</actionMapping><actionMapping>http://schemas.xmlsoap.org/ws/2005/02/rm/ AckRequested</actionMapping> ....................</operation>
If we look at the Sandesha module.xml, we will be able to learn and find out more
about writing a module.xml file.
Summary
In this chapter, you learned about the importance of the Axis2 module. We also learned that the Axis2 module provides a very fl exible way to extend the Axis2 core functionality and provides quality service. Moreover, we discussed the module and related concepts by writing a sample module and demonstrating most of the commonly used configuration settings. In our sample application, we discussed how to write handlers, how to write module implementation classes, and finally, how to put everything together and deploy the module. At the end of the chapter, we learned how to engage a module to Axis2.
In this chapter, we discussed how to write and deploy services and how to write and deploy modules. In the next chapter, we are going to discuss how to invoke a remote service. There, we will discuss how to use Axis2 as a client and invoke services in a number of ways.