There are two basic ways in which Axis is invoked:
In either case, the Axis framework's job is simply to pass the resulting MessageContext through the configured set of Handlers, each of which has an opportunity to do whatever it is designed to do with the MessageContext.
The AxisEngine's first job is to look up the transport by name. The transport is an object which contains a request Chain, a response Chain, or perhaps both. A Chain is a Handler consisting of a sequence of Handlers which are invoked in turn -- more on Chains later. If a transport request Chain exists, it will be invoked, passing the MessageContext into the invoke() method. This will result in calling all the Handlers specified in the request Chain configuration.
After the transport request Handler, the engine locates a global request Chain, if configured, and then invokes any Handlers specified therein.
At some point during the processing up until now, some Handler has hopefully set the serviceHandler field of the MessageContext (this is usually done in the HTTP transport by the "URLMapper" Handler, which maps a URL like "http://localhost/axis/services/AdminService" to the "AdminService" service). This field determines the Handler we'll invoke to execute service-specific functionality, such as making an RPC call on a back-end object. Services in Axis are typically instances of the "SOAPService" class (org.apache.axis.handlers.soap.SOAPService), which may contain request and response Chains (similar to what we saw at the transport and global levels), and must contain a provider, which is simply a Handler responsible for implementing the actual back end logic of the service.
For RPC-style requests, the provider is the org.apache.axis.providers.java.RPCProvider class. This is just another Handler that, when invoked, attempts to call a backend Java object whose class is determined by the "className" parameter specified at deployment time. It uses the SOAP RPC convention for determining the method to call, and makes sure the types of the incoming XML-encoded arguments match the types of the required parameters of the resulting method.
After the service request Chain, the global request Chain, if any, is invoked, followed by the transport. The Transport Sender, a special Handler whose job it is to actually perform whatever protocol-specific operations are necessary to get the message to and from the target SOAP server, is invoked to send the message. The response (if any) is placed into the responseMessage field of the MessageContext, and the MessageContext then propagates through the response Chains - first the transport, then the global, and finally the service.
The following diagram shows the layering of subsystems. The lower layers are independent of the higher layers. The 'stacked' boxes represent mutually independent, although not necessary mutually exclusive, alternatives. For example, the HTTP, SMTP, and JMS transports are independent of each other but may be used together.
In fact, the Axis source code is not as cleanly separated into subsystems as the above diagram might imply. Some subsystems are spread over several packages and some packages overlap more than one subsystem. Proposals to improve the code structure and make it conform more accurately to the notional Axis subsystems will be considered after v1.0 of Axis is complete.
A web service does not necessarily send a response message to each request message, although many do. However, response Handlers are still useful in the message path even when there isn't a response message, e.g. to stop timers, clean up resources, etc.
A Chain is a composite Handler, i.e. it aggregates a collection of Handlers
as well as implementing the Handler interface as shown in the following
UML diagram:
A Chain also has similarities to the Chain of Responsibility design pattern in which a request flows along a sequence of Handlers until it is processed. Although an Axis Chain may process a request in stages over a succession of Handlers, it has the same advantages as Chain of Responsibility: flexibility and the ease with which new function can be added.
Back to message processing -- a message is processed by passing through the appropriate Chains. A message context is used to pass the message and associated environment through the sequence of Handlers. The model is that Axis Chains are constructed offline by having Handlers added to them one at a time. Then they are turned online and message contexts start to flow through the Chains. Multiple message contexts may flow through a single Chain concurrently. Handlers are never added to a Chain once it goes online. If a Handler needs to be added or removed, the Chain must be 'cloned', the modifications made to the clone, and then the clone made online and the old Chain retired when it is no longer in use. Message contexts that were using the old Chain continue to use it until they are finished. This means that Chains do not need to cope with the addition and removal of Handlers while the Chains are processing message contexts -- an important simplification.
The deployment registry has factories for Handlers and Chains. Handlers and Chains can be defined to have 'per-access', 'per-request', or 'singleton' scope although the registry currently only distinguishes between these by constructing non-singleton scope objects when requested and constructing singleton scope objects once and holding on to them for use on subsequent creation requests.
A service is a special kind of Targeted Chain in which the pivot Handler is known as a "provider".
Need to explain how "FaultableHandlers" and "WSDD Fault Flows" fit in.
The EngineConfiguration interface belongs to the Message Flow subsystem which means that the Message Flow subsystem does not depend on the Administration subsystem.
The Message Flow subsystem's EngineConfiguration interface
is implemented by the Administration subsystem. FileProvider enables
an engine to be configured statically from a file containing a
deployment descriptor which is understood by the WSDDDeployment class.
SimpleProvider, on the other hand, enables an engine to be configured
dynamically.
The structure of the WSDD grammar is mirrored by a class hierarchy of factories
for runtime artefacts.
The following diagram shows the classes and the types of runtime
artefacts they produce (a dotted arrow means "instantiates").
Some of the XML elements of a SOAP message define namespaces, each in terms of a URI and a local name, and encoding styles, a standard one of which is defined by SOAP.
Header entries may be tagged with the following optional SOAP attributes:
So the SOAP message model looks like this:
Elements that we scan over, or ones for which we don't have a particular deserializer, are recorded - in other words, the SAX events are placed into a queue which may be 'played back' at a later time to any SAX ContentHandler.
Once a SOAPEnvelope has been built, either through a parse or manual construction by the user, it may be output using a SerializationContext (also see Encoding Subsystem). MessageElements all have an output() method which lets them write out their contents.
The SAX handlers form a class hierarchy:
and stack up as shown in the following diagram:
Initially, the SAX handler stack just contains an instance of EnvelopeHandler which represents the fact that parsing of the SOAP envelope has not yet started. The EnvelopeHandler is constructed with a reference to an EnvelopeBuilder, which is the SAX handler responsible for parsing the SOAP envelope.
During parsing, DCI receives the events from the SAX parser and notifies either the SAX handler on the top of its handler stack, the SAX event recorder, or both.
On the start of an element, DCI calls the SAX handler on the top of its handler stack for onStartChild. This method returns a SAX handler to be used to parse the child, which DCI pushes on its SAX handler stack and calls for startElement. startElement, amongst other things, typically creates a new MessageElement of the appropriate class and calls DCI for pushNewElement. The latter action creates the parent-child relationships of the parse tree.
On the end of an element, DCI pops the top SAX handler from its handler stack and calls it for endElement. It then drives SAX handler which is now on the top of the handler stack for onEndChild. Finally, it sets the MessageElement that is currently being deserialized to the parent of the current one.
Elements which are not defined by SOAP are treated using a SOAPHandler as a SAX event handler and a MessageElement as a node in the parse tree.
Particular serializers and deserializers are written to support
a specific XML processing mechanism such as DOM or SAX.
So serializer factories and deserializer factories
are introduced to construct serializers and deserializers
for a XML processing mechanism which is specified
as a parameter.
As is apparent from the above class diagrams, each pair of Java
type and XML data type which needs encoding
and decoding requires specific serializers and
deserializers (actually one of each per XML processing mechanism).
So we need to maintain a mapping from a pair of Java type and XML data
type, identified by a QName, to a serializer factory and a
deserializer factory.
Such a mapping is known as a type mapping.
The type mapping class hierarchy is shown below. Notice how
the default type mapping instantiates the various serializer and
deserialiser factories.
There is one final level of indirection. How do we know
which type mapping to use for a particular message?
This is determined by the encoding which is specified in
the message. A type mapping registry maintains
a map from encoding name (URI) to type mapping.
Note that the XML data type QNames are defined by the encoding.
So, in summary, to encode a Java object or primitive data value to a XML datatype or to decode the latter to the former, we need to know:
There are three layers inside the tool:
An instance of Service and its related AxisClient instance are
created before the Call object. The Call object is then created by invoking
the Service.createCall factory method. Call.setOperation creates
a Transport instance, if a suitable one is not already associated with
the Call instance. Then Call.invoke creates a MessageContext and associated
request Message, drives AxisClient.invoke, and processes the resultant
MessageContext. This significant method calls in this sequence are shown
in the following interaction diagram.
While most pluggable components infrastructures (jaxp/xerces, commons-logging, etc) provide discovery features, it is foreseen that there are situations where these may evolve over time. For example, as leading-edge technologies are reworked and adopted as standards, discovery mechanisms are likely to change.
Therefore, component discovery must be relegated to a single point of control within AXIS, typically an AXIS-specific factory method. These factory methods should conform to current standards, when available. As technologies evolve and/or are standardized, the factory methods should be kept up-to-date with appropriate discovery mechanisms.
We need to consider what's going on here. If you take a sequence of
Handlers and then introduce a distribution boundary into the sequence,
what effect should that have on the semantics of the sequence in terms
of its effects on message contexts? The following diagram shows a client-side
Handler sequence invoking a server-side Handler sequence. We need to consider
how the semantics of this combined sequence compares with the sequence
formed by omitting the transport-related Handlers.