Hi Werner,

Yeah - no problem, although I haven't done anything especially clever with
it so far, just proof of concept. 

On the Castor end is pretty straightforward. Am just using <jndi
name="java:comp/env/jdbc/castorDb" /> in my Database.xml file to point to a
DataSource, and
jdo.setTransactionManager("java:comp/env/jdbc/transactionManager") to tell
Castor to go via Tyrex to get its Connections.

The trickier bit, of course, is to have bound the transactionManager and
castorDb objects in an accessible JNDI tree beforehand. In the demo I
knocked up I just did the following (most of which is taken from code on the
Tyrex web site) :

import tyrex.tm.*;
import tyrex.resource.*;
import javax.naming.Context;
import javax.sql.DataSource;
import javax.transaction.UserTransaction;

public class MyTransactionManager {

    private static final MyTransactionManager self = new
MyTransactionManager();
    
    private RuntimeContext runCtx;
    private Context        jndiCtx;
    private TransactionDomain domain;    
    private Resources      resources;
    private DataSource     DataSourceOne;
    private DataSource     DataSourceTwo;
    
    public static MyTransactionManager getInstance() {
        return self;    
    }
    
    private MyTransactionManager() {
        goConfigure();        
    }
    
    private void goConfigure() {
        try {
            // Create a new context
            runCtx = RuntimeContext.newRuntimeContext();

            // Create the context 'comp/env/jdbc'
            jndiCtx = runCtx.getEnvContext();
            jndiCtx = jndiCtx.createSubcontext( "comp" );
            jndiCtx = jndiCtx.createSubcontext( "env" );
            jndiCtx = jndiCtx.createSubcontext( "jdbc" );

            // Obtain resources from the transaction domain            
                // see the Tyrex documentation for how to configure the
domain file and DataSources
            domain = TransactionDomain.getDomain( "MyDomain" );
            resources = domain.getResources();
            DataSourceOne =
(DataSource)resources.getResource("castorDb").getClientFactory();
            DataSourceTwo =
(DataSource)resources.getResource("otherDB").getClientFactory();
            
            jndiCtx.bind("transactionManager",
domain.getTransactionManager());
            jndiCtx.bind("userTransaction", domain.getUserTransaction());
            jndiCtx.bind("castorDb", dsDataSource);        
            jndiCtx.bind("otherDb", polDataSource);        
            RuntimeContext.setRuntimeContext( runCtx );        
        }
        catch (Throwable e) {
            throw new RuntimeException (e.toString());
        }
    }    
}

Then from a client class I was able get and start a UserTransaction using
JNDI, make a call to a Castor method in a DAO, and commit / rollback
programatically (and transparently - the client only knew the DAO interface,
and not that it was using Castor). 

Presumably how it works is that by the time the DAO called jdo.getDatabase()
the DataSource specified in Database.xml ("castorDb") would already have
been recruited by the Tyrex TransactionManager, so the Database object
returned was automatically part of the transaction. (This was bourne out by
calling isActive() on the Database object, after having started the
UserTransaction in the client.)

Also from the client I was able to look up the other DataSource, get a
Connection, and use this to execute a PreparedStatement, as part of the same
UserTransaction. The Castor operation and the PreparedStatement operation
could then be committed or rolled-back together, ie as part of a two-phase
commit.

Note that the above code is only a quick example .. having read some more
about Tyrex over the weekend then it will only work if all of the above is
on the same Thread (from calling goConfigure() through to using
UserTransaction in the client). 

Am in the middle of re-working it so that the JNDI tree can be static and
shared across Threads. Rather than call
RuntimeContext.setRuntimeContext(runCtx) just once, as in the above example,
am gonna need to have methods to set and unset this per client Thread, so
that each can be associated (and dis-associated) with the tree at runtime.
Might be an idea to have a pool of RuntimeContext objects .. will post back
on this later if anyone is interested (and as my understanding of Tyrex
increases ;-)

Incidentally my original question (in a slightly different guise ;) was
whether I could access the Tyrex JNDI tree from a remote client. So that a
UserTransaction could be started on another JVM (for instance, from a
Servlet in the Web Tier). Having looked at the code, then I don't think it's
possible by default .. the Tyrex JNDI factory doesn't seem to cater for
lookup by URL (the "provider_url" string is only to identify a shared vs
private instance of the MemoryContext). 

However I don't think it would be too difficult to turn MyTransactionManager
into an RMI server so that a remote client's Thread (or its representation
in the remote JVM, at any rate) could get associated with the Tyrex
RuntimeContext, and (hopefully) gain access to the JNDI tree. 

Might even be an idea to incorporate this kind of thing into the Tyrex
ContextFactory implementation so that provider_url can do the necessary, but
my understanding of all of this stuff is still fairly basic so this might
turn out to be a silly idea ;-) But any comments / advice would still be
appreciated (!)

Hope this is of use, and apologies if it's a bit of a ramble.

John

----------------------------------------------------------- 
If you wish to unsubscribe from this mailing, send mail to
[EMAIL PROTECTED] with a subject of:
        unsubscribe castor-dev

Reply via email to