Abstraction of the intermediate layers in the processing chain
and transport.
What is a
Tube ?
Tube is a basic processing unit that represents SOAP-level
protocol handling code. Mutliple tubes are often put together in
a line (it needs not one dimensional — more later), and act on
Packet s in a sequential fashion.
Tube s run asynchronously. That is, there is no guarantee that
Tube.processRequest(Packet) and
Tube.processResponse(Packet) runs
in the same thread, nor is there any guarantee that this tube and next
tube runs in the same thread. Furthermore, one thread may be used to
run multiple pipeline in turn (just like a real CPU runs multiple
threads in turn.)
Tube examples
Transport is a kind of tube. It sends the
Packet through, say, HTTP connection, and receives the data back into another
Packet .
More often, a tube works like a filter. It acts on a packet,
and then it tells the JAX-WS that the packet should be passed into another
tube. It can do the same on the way back.
For example, XWSS will be a
Tube . It will act on a request
Packet , then perhaps wrap it into
another
Packet to encrypt the body and add a header, then
the processing will go on to the next tube.
Yet another kind of filter tube is those that wraps
LogicalHandler and
SOAPHandler . These tubes are heavy-weight; they often consume
a message in a packet and create a new one, and then pass it to the next tube.
There would be a
Tube implementation that invokes
Provider .
There would be a
Tube implementation that invokes a service method
on the user's code.
There would be a
Dispatch implementation that invokes a
Tube .
WS-MEX can be implemented as a
Tube that looks for
Message.getPayloadNamespaceURI and serves the request.
Tube Lifecycle
Pipeline is expensive to set up, so once it's created it will be reused.
A pipeline is not reentrant; one pipeline is used to process one request/response
at at time. The same pipeline instance may serve multiple request/response,
if one comes after another and they don't overlap.
Where a need arises to process multiple requests concurrently, a pipeline
gets cloned through
TubeCloner . Note that this need may happen on
both server (because it quite often serves multiple requests concurrently)
and client (because it needs to support asynchronous method invocations.)
Created pipelines (including cloned ones and the original) may be discarded and GC-ed
at any time at the discretion of whoever owns pipelines. Tubes can, however, expect
at least one copy (or original) of pipeline to live at any given time while a pipeline
owner is interested in the given pipeline configuration (in more concerete terms,
for example, as long as a dispatch object lives, it's going to keep at least one
copy of a pipeline alive.)
Before a pipeline owner dies, it may invoke
Tube.preDestroy() on the last
remaining pipeline. It is "may" for pipeline owners that live in the client-side
of JAX-WS (such as dispatches and proxies), but it is a "must" for pipeline owners
that live in the server-side of JAX-WS.
This last invocation gives a chance for some pipes to clean up any state/resource
acquired (such as WS-RM's sequence, WS-Trust's SecurityToken), although as stated above,
this is not required for clients.
Tube and state
The lifecycle of pipelines is designed to allow a
Tube to store various
state in easily accessible fashion.
Per-packet state
Any information that changes from a packet to packet should be
stored in
Packet (if such informaton is specific to your problem domain,
then most likely
Packet.invocationProperties .)
This includes information like transport-specific headers.
Per-thread state
Any expensive-to-create objects that are non-reentrant can be stored
either in instance variables of a
Tube , or a static
ThreadLocal .
The first approach works, because
Tube is
non reentrant. When a tube is copied, new instances should be allocated
so that two
Tube instances don't share thread-unsafe resources.
Similarly the second approach works, since
ThreadLocal guarantees
that each thread gets its own private copy.
The former is faster to access, and you need not worry about clean up.
On the other hand, because there can be many more concurrent requests
than # of threads, you may end up holding onto more resources than necessary.
This includes state like canonicalizers, JAXB unmarshallers,
SimpleDateFormat , etc.
Per-proxy/per-endpoint state
Information that is tied to a particular proxy/dispatch can be stored
in a separate object that is referenced from a tube. When
a new tube is copied, you can simply hand out a reference to the newly
created one, so that all copied tubes refer to the same instance.
See the following code as an example:
class TubeImpl {
// this object stores per-proxy state
class DataStore {
int counter;
}
private DataStore ds;
// create a fresh new pipe
public TubeImpl(...) {
....
ds = new DataStore();
}
// copy constructor
private TubeImpl(TubeImpl that, PipeCloner cloner) {
cloner.add(that,this);
...
this.ds = that.ds;
}
public TubeImpl copy(PipeCloner pc) {
return new TubeImpl(this,pc);
}
}
Note that access to such resource may need to be synchronized,
since multiple copies of pipelines may execute concurrently.
VM-wide state
static is always there for you to use.
See Also: AbstractTubeImpl See Also: AbstractFilterTubeImpl author: Kohsuke Kawaguchi author: Jitendra Kotamraju |