Hi Marc,
marc fleury wrote:
>
> |Class org.jboss.ejb.MethodInvocation is also
> |changed. A class variable referring to an
> |instance of the TPC Importer is added. This
> |will be filled in by an instance the first
> |time needed. An extra constructor that takes
> |a TPC is added, and used when the MI is created
> |from a RMI. Internally in MI, the TX and TPC
> |are seperate private variables. The method
> |getTransaction() is changed so that if the TX
> |is null and the TPC is not null, the TPC Importer
> |is called to create a TX. This TX is set in the
> |MI and returned to the caller.
> |This way, changes are transparent to the container
> |and its interceptors. Only the container invoker
> |has to use the new extra constructor when creating
> |a MI from a RMI.
> |And this also means that a transaction will *not*
> |be imported unless it needs to (MI.getTransaction()
> |called). When we get distributed transactions, the
> |import can be expensive. Currently, the TX
> |interceptors call MI.getTransaction() every time,
> |but changing them to do it only when needed should
> |be easy.
>
> Ok, I believe the "import" can be even more lazy. If you are talking about
> a real "import" as in an association with the thread then the
> MI.getTransaction() is a bit strong to do that. Remember that the existence
> of a transaction is only needed in the MI as long as you are in stacks that
> are "MI-type" aware. Only when you leave these stacks (say to call an
> instance) do you have to associate the transaction with the thread (import)
> and pass that on.
>
> In other words. Unless you give another meaning to "import" I believe the
> "import" is something container configuration specific and that your "TPC
> importer" doesn't belong in the "message" it is a worker on the assembly
> line not a "central MI feature". Factor it lazily is my recommendation.
The TransactionPropagationContextImporter does
no thread association at all. It has only one
purpose: Given a transaction propagation
context (a serializable Object instance
suitable for transmitting across the wire),
it must deliver a reference to an instance
implementing javax.transaction.Transaction
that represents the transaction in the local
VM. The interface has a single method:
public interface TransactionPropagationContextImporter
{
public Transaction importTransactionPropagationContext(Object tpc);
}
Depending on the actual implementation of
this interface, the transaction may or may
not really be imported. If the same
transaction has already been imported into
the local VM, it would make sense to avoid
the slow network calls involved with the
import and just look up a local Transaction
instance in a map. The current in-VM DTM
always does this.
I have a reason for doing this in the MI
instead of directly in the RMI: If the
transaction is never used, there is no need
to import it. With a real DTM, the import
includes expensive (in terms of latency)
network calls to register with a transaction
service. If we can avoid it, we may get
a significant performance gain, but we do
not know if we can avoid it until we are
in the interceptor chain.
With this implementation, a simple change
of the transaction container interceptors
to only call MI.getTransaction() if the
propagated transaction is really needed,
may save the time of a remote call to a
transaction service when the propagated
transaction context is not needed.
To put things into the JTA terminology:
The TPC Factory and Importer interfaces
define the communication resource manager
(CRM) that the JTA does not specify
except for mentioning JTS for
interoperability (standard wire protocol).
In JTS, package org.omg.CosTSPortability
does the job of the CRM. The two
interfaces I made for this are a little
simpler, as I prefer to keep things simple,
and we generally do not need the complexity
introduced by CORBAs Dynamic Invocation
Interface. In the few special cases where
it might be needed (like CORBA deferred
invocation with transaction contexts under
a non-JTS transaction service), it should
be possible to add a little extra code to
the non-JTS TM support it. And we won't
even have this problem until we have a
non-JTS distributed TM capable of exporting
transactions to the CORBA OTS domain.
But still, no thread association here.
That is up to the application server and
comes below:
> |Another thing that binds JBoss to the current
> |native TM is the use of the non-JTA methods
> |associateThread() and disassociateThread. As a
> |proparation for avoiding these calls, I've
> |implemented the JTA methods suspend() and
> |resume(). When JBoss has been changed to use
>
> We have been there before and it is a mistake.
Well, judging from code comments, we do not
quite agree here. I am just not sure why.
Guess this happened before I got involved with
JBoss.
> suspend() and resume() are
> methods ON THE TRANSACTION (suspend the transaction and resume the
> transaction), suspend association and resume association are ENTIRELY
> DIFFERENT semantics, they suspend and resume teh association but NOT THE
> TRANSACTION. It is a spec missread.
I think that the method names suspend() and
resume() are misleading:
Reading JTA 1.0.1, section 3.2.3, it is clear
that thread association *is* done in these
methods.
But given these method names one would think
that transaction suspension and resumption
is also done here. This is not really the
case, as JTA 1.0.1, section 3.2.3 specifically
says that the application server is responsible
for calling XAResource.end(TMSUSPEND) and
XAResource.start(TMRESUME) on the right
resources at the right times.
JTA 1.0.1, section 3.2 says: "The
javax.transaction.TransactionManager interface
allows the application server to control
transaction boundaries on behalf of the
application being managed. For example, the EJB
container manages the transaction states for
transactional EJB components; the container
uses the TransactionManager interface mainly to
demarcate transaction boundaries where
operations affect the calling thread's
transaction context. The Transaction Manager
maintains the transaction context association
with threads as part of its internal data
structure. A thread's transaction context is
either null or it refers to a specific global
transaction. Multiple threads may concurrently
be associated with the same global transaction."
I cannot really read this as anything but:
"Thread association done here".
And since section 3.2.3 (about these two
methods) also says "Thread association done
here" and "Resource tx suspension is caller's
responsibility", I find it hard to read the
JTA spec any other way than "the method names
are misleading", and "these methods do (almost)
what the JBoss-specific thread association
does".
I think I am right here, but I may still
misread JTA.
If you think so, please let me know.
> In fact the interface is there to "debind" JBoss as you can use whatever is
> needed to reassociate with the thread under that generic interface (in JOnAS
> it was the "setPropagationContext" something that couple the idea of your
> TPC and the association at the same time).
Looking at some old JonAS source, it looks like
these methods (in org.objectweb.jonas.jtm.Current)
do the thread association and "some more":
This "some more" is calling res.end(TMSUSPEND)
and res.start(TMRESUME) on the resources from
a private list of XAConnections that are
modified in JonAS specific methods
Current.addConnection(..) and removeConnection(..).
So if we do not use these JonAS-specific methods
for enlisting and delisting XAConnections and just
do like JTA requires of the application server, I
guess the JonAS TM should be OK with this when
used in JBoss.
I guess I should add a few comments on the
requirement of JTA that the application server
suspend resources when a transaction is suspended:
I am not really sure why this is required. When
some component in the application server calls
another component is such a way that a TM.suspend()
is needed (according to JTA), any XA resource
connections in the first component are not used in
the second component, so an XA resource suspend
should not be needed. At least this seem to be how
JBoss works now. I wonder if this requirement is
meant for some kind of resource connection sharing
between conponents that I do not know about.
Best Regards,
Ole Husgaard.