Apache Axis2 Web Services

March 11, 2011

Webservices

«»

 

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.

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 fl exible 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.

1
2
3
4
5
6
7
8
9
10
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.

1
2
3
4
5
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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:

  1. First get the configurationContext from the messageContext.
  2. Get the property value specified by the property name.
  3. Then increase the value by one.
  4. Next set it back to configurationContext.
  5. 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
  6. The message cannot continue as some of the conditions are not satisfied yet,
    so you need to pause the execution and wait.
  7. InvocationResponse.ABORT.
  8. 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:

1
2
3
4
5
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
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.

Webservices Articles & Books

email

«»

Comments

comments