![]() ![]() |
The Axis ArchitectureFrom the start, Axis was designed with a completely open and pluggable architecture. In its simplest form, Axis can be viewed as a thin layer that sits between the business logic and the network transport carrying your data. As depicted in Figure 4.1, Axis is simply the means by which the SOAP message is taken from a transport (such as HTTP) and handed to the Web service and the means by which any response is formatted as a SOAP message to then be sent back to the requestor. Although this might seem like an oversimplification, Axis is designed to be used in a wide range of environments and by deployment engineers with varying skill levels. When you're first experimenting with Web services, or if you don't need complex configurations, Axis by default will make the deployment of Web services very easy. The "Simple Web Services" section will describe this further by showing you how to quickly take Java code and deploy it as a Web service in Axis. However, if you need a more complex processing model, Axis can be configured to support that, too. Figure 4.1. Basic overview of Axis.Axis ComponentsNext, we're going to focus on the various components of Axis. Each item in the following list will be discussed in more detail, but here's brief summary of the key components:
Axis EngineAs you could probably guess, the Axis engine is the focal point of the Axis SOAP processor. The engine's job is to act as the main entry point into Axis' message processing model as well as to coordinate the SOAP message's flow through the various components. The engine is also responsible for ensuring that the SOAP semantics are followed—for example, it will verify that the mustUnderstand checks are properly performed. In the following sections, we'll discuss the other components that make up the processing model, but it is important to know that the engine is the piece responsible for coordinating the order in which those other components are invoked. As we discuss each of these components, we'll describe their interaction with the engine in more detail. During the design process of Axis, it was realized that it would be impossible to design a SOAP message processor in such a way that it could work for the wide-ranging uses we wanted for Axis unless it was flexible enough to allow deployment engineers (configuration administrations) to control the message flow itself. Allowing people to tell Axis which message processing logic to perform, in what order, and when, became a clear requirement. It also became apparent that there would be no way of knowing how people would want to process the SOAP messages. For example, many people see SOAP as simply another Remote Procedure Call (RPC) mechanism—which is a valid use. However, there is no reason the exact same SOAP message that might be treated as an RPC message by one SOAP processor could not be treated as an XML document and simply run through an XSLT processor by another. As long they both adhere to the SOAP specification, and of course follow the semantic rules defined by the Web service definition, exactly how the SOAP message is processed is wide open. So, how does Axis handle these challenges? Handlers and chains. Handlers and ChainsAt its most basic level, Axis is all about chaining together pieces of message processing logic. Figure 4.1 shows just one piece: the Web service itself. Often, you'll need to perform additional processing on the message either before or after the Web service itself is invoked. For example, some logging might need to take place, or SOAP headers might need to be processed. You can accomplish this two ways: Place this additional logic in the Web service itself or allow for additional pieces of code to be executed outside of, but before and after, the Web service. For this purpose, Axis has the notion of chains. Chains are simply ordered collections of components (code) that Axis will invoke sequentially and in the order specified. The components that are used to build these chains are called handlers. Each handler has the opportunity to examine or modify the SOAP message in order to complete its job. For example, it is possible to have a handler in the chain that will look for any encrypted data in the message and decrypt it before the Web service itself is invoked (SOAP encryption is explained in more detail in Chapter 5, "Using SOAP for e-Business"). By modularizing the work in this way, each handler is free to focus on its core job and not worry about any possible auxiliary work that might need to be done, thereby eliminating code duplication. Also, if new pre- or post-processing is needed in the future, it becomes a simple matter of plugging new handlers into the chain through configuration changes to Axis rather than having to make code changes to the Web service. This also allows third-party vendors to produce Axis handlers that can be snapped into any configuration without prior knowledge of the exact environment, configuration, or Web service being invoked. Handlers are the basic building blocks inside Axis. Everything, even the Axis engine and the chains themselves, is a handler. As a result, the deployment engineer is free to configure Axis in an unlimited number of ways. It is possible for a preprocessing handler shown in Figure 4.2 to be a chain that itself contains a collection of handlers (or even more chains). Aside from each handler having access to the SOAP message, each handler can also be involved in the production of any possible SOAP response message. We'll give more details in the "Building Handlers" section, but it is worth reiterating that each handler does have access to (and can change) the request message Figure 4.2. Pre- and post-processing is done by defining a chain.Chains are the mechanisms by which handlers are grouped together. The concept is simple; a chain is an ordered collection of handlers that together can be viewed as a single unit of processing. As with any good rule, there is a slight complication—certain types of chains have the notion of a pivot point Figure 4.2 shows that it is possible to define a chain of handlers that are invoked for a particular Web service. However, what if chains need to be invoked for all Web service invocations flowing through Axis? Or, what if certain chains need to be invoked only if the message came in on HTTP, whereas if the message was delivered via SMTP those chains should not be invoked? To support these configurations, Axis has the notion of transport specific chains As shown in Figure 4.3, Axis allows the definition of chains that are invoked based on the type of transport used in the delivery of the SOAP message (transport specific chains) and chains that should be invoked for all Web services (global chains). Figure 4.3. Axis includes three different levels of chaining.Transport-specific chains might be defined to process such things as transport-specific compression, or authentication. The "Configuration Methods" section will discuss the way to deploy chains, but it is important to note that transport specific chains are targeted-chains. The request and response sides are clearly separated so that the Axis engine knows exactly which subset of handlers to invoke at the start of the message flow and which set to invoke at the end of the message flow. Although it was possible to design Axis so that there were two completely separate chains defined (rather than one with a request and a response side), it was decided that because they would always be used in conjunction with each other, and because it is expected that there will be a large number of transport specific chains, it would be easier from a usability point of view to define them as one chain with two sides. Global chains come in handy in cases when all Web services require the same processing regardless of how the message was delivered or what the specific Web service itself is—for example, SkatesTown might want to keep a log of all Web service requests, and placing a logging handler in the global chain becomes a cleaner way of deploying it than placing it in each Web service–specific chain. Unlike transport chains that use targeted-chains, the global chain is split into two separate chains. Because, conceptually, there is only one global chain, it was decided that it would be easier for deployment engineers to define two separately named chains (global.request and global.response) rather than one chain with two sides. We'll discuss the details of how to name chains in the "Configuring Axis" section. Within the Axis server-side engine, the order of chain processing is as follows:
This processing model should allow a deployment engineer to have the complete flexibility of placing any handler at any point in the flow of messages through the server Axis engine. In Figure 4.3, the Web service is shown as a handler at the pivot point in the Web service– specific chain. One layer of code is not shown: the dispatcher Figure 4.4 shows a dispatcher as the pivot point handler in the Web service–specific chain. Like all handlers, this dispatcher is responsible for acting as the bridge between Axis and your business logic. It is the job of the dispatcher to locate and invoke the appropriate piece of code associated with the desired Web service. For example, Axis comes with an RPCDispatcher Figure 4.4. Handlers, acting as dispatchers, are the bridges between Axis and application logic.Although dispatchers have been presented in the context of invoking the Web service, any of the handlers in any of the chains could be dispatchers. In other words, there is no reason why any of the request/response processing handlers need to have the business logic directly inside them. Those handlers could be dispatchers that extract the necessary data from the SOAP message and pass it along to external code in the proper format. TransportsIn order to complete the overall picture of the Axis architecture, one more piece of the puzzle needs to be brought in: transports. All the figures show a single Axis engine with a single transport delivering the SOAP message; however, this need not be the case. Figure 4.5 shows that Axis can support a variety of transports, not just HTTP. Actually, Axis itself doesn't support multiple transports. Axis is designed such that the Axis engine can be viewed simply as a chunk of code that is called with an incoming message and returns an outgoing message. Transport listeners Figure 4.5. Multiple transports.The role of the transport listener is to deliver the SOAP message to the Axis engine. This could mean listening on port 80 for an HTTP request or waiting for a file to be FTPed to a certain directory and then handing that message to the Axis engine. Aside from invoking the Axis engine, the transport listener must also tell the Axis engine which transport was used—this allows Axis to invoke any transport-specific chain that might have been configured. The delineation between whether certain processing should be in the transport listener or in the transport specific chain is an issue left up to the deployment engineer. For example, in the HTTP case, the servlet waiting for SOAP requests can also perform any basic HTTP authentication checks before invoking the Axis engine. It is also perfectly acceptable for the servlet to not perform that check and leave it up to a handler on the transport specific chain to do it. The choice is left open. Listing 4.1 shows a sample transport listener that does nothing more than look for a file called inMsg in the current directory. If the file is found, the transport listener uses it as the requesting SOAP message, calls Axis on it, and then places any response back into a file called outMsg. Listing 4.1 FileListener.javaimport java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileNotFoundException; import org.apache.axis.AxisFault; import org.apache.axis.AxisEngine; import org.apache.axis.server.AxisServer; import org.apache.axis.Message; import org.apache.axis.MessageContext; import org.apache.axis.message.SOAPEnvelope; import org.apache.axis.message.SOAPFaultElement; public class FileListener { public void run() { while (true) { try { // Look for an incoming msg, create a new Message object FileInputStream input = new FileInputStream("inMsg"); AxisEngine engine = AxisServer.getSingleton(); MessageContext msgContext = new MessageContext(engine); Message msg = new Message(input); try { //Set it as the "request" message msgContext.setRequestMessage(msg); // Set the Transport msgContext.setTransportName("file"); // Invoke the Axis engine engine.invoke(msgContext); } catch(Exception e) { // Catch any error and stick it in the response if (!(e instanceof AxisFault)) e = new AxisFault(e); AxisFault af = (AxisFault)e; msg = msgContext.getResponseMessage(); if (msg == null) { msgContext.setResponseMessage(new Message(af)); } else { SOAPEnvelope env = msg.getAsSOAPEnvelope(); env.clearBody(); env.addBodyElement(new SOAPFaultElement(af)); } } // Close and delete the incoming message input.close(); (new File("inMsg")).delete(); // Place the response message in a file called "outMsg" msg = msgContext.getResponseMessage(); FileOutputStream output = new FileOutputStream("outMsg"); String result = (String)msg.getAsString(); output.write(result.getBytes()); output.close(); System.out.println("Processed a request"); } catch(Exception exp) { if (!(exp instanceof FileNotFoundException)) exp.printStackTrace(); } // Sleep for a sec and then loop try { Thread.sleep( 1000 ); } catch(Exception e) { } } } static public void main(String[] args) { (new FileListener()).run(); } } Let's walk through this example. The FileListener's run() method will loop forever while waiting for a new message to arrive. Once there, an InputStream is created for it. The listener will then ask Axis for an instance of an AxisServer—the getSingleton() method will return the same instance each time. Next we create a MessageContext The rest of the code processes any result from the engine. We must, of course, handle any error conditions. The Axis engine can throw an AxisFault In either the successful case or the faulting case, the code will then get the response message as a String, write it out to a file called outMsg, and then go look for more messages. Completing the overall picture of what the Axis engine's architecture looks like, we have Figure 4.6. Figure 4.6. Complete Axis architecture.Although the final picture of the Axis engine's processing model might seem a bit complex, it really is nothing more than defining handlers and chains. Of course, we've overlooked one very important issue; up to this point, the focus has been on what an Axis engine does when it is on the receiving side of the SOAP message path (the server). Axis can be used on the client side, as well. With just a few slight changes, all the concepts we've mentioned up to now apply on the client as shown in Figure 4.7. Figure 4.7. Axis client architecture.It should be clear that almost all the same components that appeared on the server are on the client as well; they are just invoked in a slightly different order:
As mentioned in Step 3, there is something new here: a transport sender. A transport sender is a handler that is responsible for taking the request SOAP message and sending it to a SOAP server. For example, one of the transport senders that is shipped with Axis is an HTTP transport sender that will take the request SOAP message, open an HTTP socket connection to the specified HTTP server, do a POST of the SOAP message, and wait for a response. The response is then placed in the response message portion of the MessageContext object. Because transport senders are just handlers, they can be placed in any chain in any configuration. So, it is technically possible that the transport chain shown in Figure 4.7 could have multiple transport senders if you desire a multicast scenario. It is also possible that a transport sender could be placed in one of the chains on the server such that the response message could be sent over a different transport than it was received on (for example, the transport listener could be an HTTP servlet, but the SOAP response message could be sent back via SMTP). Locating the Service ChainWe've talked about how there are service-specific chains, but we haven't yet touched on how the service chain is selected by the Axis engine. An interesting aspect of SOAP is that it doesn't mandate how to determine the exact Web service to invoke. This might seem odd—but it is accurate. Many different common practices have been established, and each one is valid. Here are just three of the more common ones:
Axis could have chosen one method, most likely the namespace approach, but doing so would have limited the options available to its users. Instead, Axis lets you choose how services are determined. A handler can be written and placed in the transport chain or global chain that can determine which service chain to choose. This handler is free to use any algorithm it wants to make this determination. However, if a service chain has not been selected by the time the Axis engine gets to the point in its processing that it wants to invoke the service specific chain, it will default to the namespace selection method. XML ParsingAxis has been designed and coded with a watchful eye towards performance. For this reason, when tackling the problem of how to parse an XML stream efficiently, a SAX-based approach was chosen over a DOM-based one. As we discussed in Chapter 2, "XML Primer," SAX XML parsing does not read the entire XML stream into memory; rather, it triggers callbacks based on the types of XML tokens that are encountered. It then becomes the responsibility of those callback routines to do any buffering of data or saving of state that is needed so that subsequent callbacks still have access to any previously seen data. Even though SAX is used for parsing, a DOM-based representation of XML sometimes is used when passing around XML blocks—as will be seen in the "Document-Centric Services" section. In various discussions and examples in this chapter, we'll need to talk about concepts and features in terms of using a SAX parser or writing SAX callback routines. We assume that you are familiar with these concepts, and we will not explain the specific details of how to write code utilizing a SAX parser in great detail. With that overview of what Axis is and how it works, now we can move on to actually using it. ![]() |
![]() ![]() |