[axis2] finalizing context stuff

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[axis2] finalizing context stuff

Sanjiva Weerawarana
During the last F2F we came up with a hierarchical set of contexts to
store all state of the system. We spent some time in Colombo on Friday
to flesh that out in the code. The code has been mostly updated to be in
sync with this.

======

Let's start with the WSDL description. At runtime, we have the following
classes representing the metadata of the service. Note that these
classes represent the service - not running "instances" of a service.

- AxisMessage
      Description of the input, output and fault messages that are
described in the service's WSDL.

- AxisOperation
      Description of operations. This contains pointers to 1..n
AxisMessage objects.

- AxisService
      Description of the entire service. This contains pointers to 1..n
AxisOperation objects.

- AxisServiceGroup
      (This is new.) WSDL 2.0 requires that a service only implement one
interface. However, there are many scenarios where one or more services
work together - for example a service may have a callback service that
needs to be sent to someone else (think of an eventing scenario for
example). So the idea of AxisServiceGroup is to represent metadata about
a collection of related services. This contains pointers to 1..n
AxisService objects.
      Note that we currently don't have a way to package and deploy a
collection of services as a "group."
      What we propose is that for M2 this layer be left out. So this is
brought up primarily for discussion; I don't believe there's any code
yet.

- AxisSystem
      (This is what used to be called EngineConfiguration.) The
AxisSystem is the runtime representation of deployment info - the
deployed (groups of) services, available modules, transports, message
receivers etc.. Thus, it contains pointers to 1..n AxisServiceGroup
objects.
      Note that we tried to come up with a name that had the pattern
Axis<XYZ> .. hence the attempt to change the name.

======

Now, all of this is the metadata level. Corresponding to this, we need
to be able to keep track of running "instances" of these. So, we have:

- MessageContext
      Represents one message as its flowing thru the system. This is a
familiar beast. In addition to the data it contains, it also has a
pointer to the AxisMessage it is an "instance" of.
      In addition, it contains a pointer to the OperationContext under
which it comes into existence.

- OperationContext
      Represents the state of a running operation. This is needed
because in WSDL2 land operations can be arbitrarily complex. So while an
"operation" (which really should've been called an interaction) is in
progress, the OperationContext captures the state of the interaction -
like which slots (messageLabels) to assign to messages etc..
      The default OperationContext class can be used to handle all of
the WSDL 1.1 operation patterns and many of the standard WSDL 2.0 MEPs.
However, if a custom MEP is to be supported, then someone has to write a
new OperationContext class that knows how to handle that MEP and extend
the OperationContextFactory to be aware of how to create that
OperationContext.
      The OperationContext contains a pointer to the AxisOperation of
which it represents a running instance. It also contains a pointer to a
ServiceContext object, under whose purview it lives.

- ServiceContext
      This represents an interaction with a service, consisting of a
group of operation interactions. That is, the service context captures
any information that lives across a set of operation invocations. For
example, if a specific receiver were to create a new instance of a class
for each set of interactions, then the ServiceContext would be a fine
and dandy place to keep a pointer to that class.
      The ServiceContext contains a pointer to the AxisService of which
it represents a running instance. It also contains a pointer to a
ServiceGroupContext, under whose purview if lives.

- ServiceGroupContext
      This represents a collection of services which are acting in
concert to offer some functionality. This is basically what Glen said
Axis1's Session concept it .. it captures a set of service interactions.
So we tried to come up with a consistent name and came up with
ServiceGroupContext.
      The ServiceGroupContext contains a pointer to the AxisServiceGroup
of which it represents a running instance. It also contains a pointer to
a SystemContext, under whose purview it lives.
      Again this code is not there yet and the proposal is to skip this
layer for M2.

- SystemContext
      (This used to be called EngineContext.) This represents the
runtime configuration state of a running AxisEngine. This contains a
pointer to the AxisSystem object of which it represents a running
instance. (Again this is the same relationship that was there before
with EngineConfiguration and EngineContext; what we attempted to do was
follow the AxisXYZ and XYZContext pattern.

=======

Thilina drew a nice figure to represent this model. Please see attached
figure contextArchitecture.jpg.

=======

Looking for stuff:

We discussed how the various levels of contexts will be chained together
to support setting properties at any level. This works very nicely with
the following model:

- look up the *Context instance parent chain first
      So, start with MessageContext, then OperationContext, then
ServiceContext etc. until SystemContext
- if not found, then search up the metadata hierarchy
      Search starting with AxisMessage until found (starting at the
right level obviously).

This allows any property to be overridden at any level either
service-wide or "instance" wide. In fact, this is enough to implement
fully dynamic policy driven stuff we believe .. of course the plan is to
implement only static policy driven stuff right now; nothing dynamic.

=======

Providing alternate implementations:

All contexts implement the same interface via a single base class. It is
possible to switch impls, like what Geoffrey wanted to do to, to plug in
a higher fidelity impl (e.g., one that does migration etc. to make this
work across a cluster or grid of machines). I think we need to introduce
a factory etc. to make this work fully but the architecture supports
doing so easily.

=======

Routing incoming messages:

Let's see how this works with incoming messages:

- message comes in via a servlet or whatever and we create a
MessageContext (whose AxisMessage and OperationContext pointers are
still null).

- identify AxisServiceGroup and AxisService using WS-Addr <To> or
SOAPAction or HTTP POST URL or whatever technique (done by a
pre-dispatch handler)

- now need to identify ServiceGroupContext using any of ID in the URL,
cookie, WS-Addr ref properties or whatever technique.
 
- find ServiceContext by looking up the service name in the identified
ServiceGroupContext
      If these two are not found, then new ServiceContext and/or
ServiceGroupContext need to be created. (Think of the servlet model ..
if no cookie create a new session; these are sessions are are creating
but ones that can potentially live a lot longer than an HTTP session.)

- identify AxisOperation to which this message is targetted

- look up or create OperationContext - by looking in the ServiceContext
to see whether there's an OperationContext to which this message is
related (using wsa:RelatesTo) and if not create one. [NOTE: This is how
an incoming response message gets correlated to a pending request.]

- complete the pointers to make sure MessageContext points to
OperationContext etc.. and then continue (run thru the modules and
deliver the message to the MessageReceiver).


In order to implement this, we need to keep a bit of info around:
- a map from MessageID to OperationContext, which can be kept at
AxisOperation or globally as the MessageIDs must be globally unique.
This allows us to find the OperationContext to which a message
contributes (by looking up the related MessageID).

- a map from ServiceGroupID (or ServiceID for now) to
ServiceGroupContext (or ServiceContext for now). The ID itself can be a
cookie, a reference property, a part of the URL etc. - whatever really.
Some mechanism is needed however to correlate a bunch of operation
invocations into a single bucket. (I believe this is the same as Axis1's
Session stuff.) This map needs to be stored in the SystemContext.

- a map from service name to ServiceContext, stored in the
ServiceGroupContext. This allows us to find the specific ServiceContext
for a given service within a ServiceGroup. (Again, differed until
further agreement.)

======

Configuring outgoing messages:

We've also spent some time to try to very precisely organize the client
side. I have a half written email about that too which I will try to
finish and send tonite if at all possible.

My sincere apologies for not being more proactive on the email front.

======

The beauty of this approach is its consistency. It turns out the same
*Context stuff makes sense on the client side too. That is, in order to
capture the state of an operation, we can use the OperationContext
within a method of a stub. To store state across operations, we can use
a ServiceContext as an instance var in the class.

The challenge of course will be to implement this efficiently. I believe
it can be done by taking shortcuts whenever possible for the most common
cases, yet not destroying the architecture and orthogonality of the
design.

Hope this helps!

Sanjiva.

contextArchitecture.jpg (44K) Download Attachment