Atomikos Forum

Spring + Junit integration

Hi,  I have seen a few other people run into the problem where datasource beans with "duplicate" unique resource names are trying to be registered with atomikos.  There are numerous contributors to this problem, but the main one is that Spring does not shut down old application contexts until after testing is complete, meaning configurations that are shared across tests will end up being used with the same atomikos instance.

I came up with the following as a workaround:

1) Use this object factory - does a rebind in the case of duplicate resource name:

public class ReusableIntraVmObjectFactory extends IntraVmObjectFactory {

    private static final String NAME_REF_ADDRESS_TYPE = "uniqueResourceName";

    public static synchronized Reference createReference(Serializable object,
            String name) throws NamingException {
        Reference ret = null;
        if (object == null)
            throw new IllegalArgumentException("invalid resource: null");
        if (name == null)
            throw new IllegalArgumentException("name should not be null");

        // make sure that lookup works - add the bean to the registry if needed
        try {
            Object existing = IntraVmObjectRegistry.getResource(name);
            if (existing != object) {
                // another instance with the same name already there
                IntraVmObjectRegistry.removeResource(name);
                IntraVmObjectRegistry.addResource(name, object);
            }
        } catch (NameNotFoundException notThere) {
            // make sure this bean is registered for JNDI lookups to find the
            // same instance
            // otherwise, concurrent lookups would create race conditions during
            // init
            // and the thread that creates a bean might not be able to use it
            // (unfair?)
            IntraVmObjectRegistry.addResource(name, object);
        }
        ret = SerializableObjectFactory.createReference(object,
                IntraVmObjectFactory.class.getName());
        // also add the unique resource name for helping during retrieval
        ret.add(new StringRefAddr(NAME_REF_ADDRESS_TYPE, name));
        return ret;
    }
}

2) Use this data source bean - does a Configuration.removeResource before Configuration.addResource in doInit() (same basic change for the jms ConnectionFactory bean as well):

public class ReusableAtomikosDataSourceBean extends AtomikosDataSourceBean {

    private static final long serialVersionUID = 5304850013250517530L;

    public Reference getReference() throws NamingException {
        return ReusableIntraVmObjectFactory.createReference(this,
                getUniqueResourceName());
    }

    protected com.atomikos.datasource.pool.ConnectionFactory doInit()
            throws Exception {
        if (getXaDataSource() == null) {
            if (getXaDataSourceClassName() == null)
                throwAtomikosSQLException("Property 'xaDataSourceClassName' cannot be null");
            if (getXaProperties() == null)
                throwAtomikosSQLException("Property 'xaProperties' cannot be null");
        }

        Configuration.logInfo(this + ": initializing with ["
                + " xaDataSourceClassName=" + getXaDataSourceClassName() + ","
                + " uniqueResourceName=" + getUniqueResourceName() + ","
                + " maxPoolSize=" + getMaxPoolSize() + "," + " minPoolSize="
                + getMinPoolSize() + "," + " borrowConnectionTimeout="
                + getBorrowConnectionTimeout() + "," + " maxIdleTime="
                + getMaxIdleTime() + "," + " reapTimeout=" + getReapTimeout()
                + "," + " maintenanceInterval=" + getMaintenanceInterval()
                + "," + " testQuery=" + getTestQuery() + "," + " xaProperties="
                + printXaProperties() + " loginTimeout=" + getLoginTimeout()
                + "]");

        if (getXaDataSource() == null) {
            Class xadsClass = null;
            try {
                xadsClass = ClassLoadingHelper
                        .loadClass(getXaDataSourceClassName());
            } catch (ClassNotFoundException nf) {
                AtomikosSQLException
                        .throwAtomikosSQLException(
                                "The class '"
                                        + getXaDataSourceClassName()
                                        + "' specified by property 'xaDataSourceClassName' could not be found in the classpath. Please make sure the spelling is correct, and that the required jar(s) are in the classpath.",
                                nf);

            }
            Object driver = xadsClass.newInstance();
            if (!(driver instanceof XADataSource)) {
                AtomikosSQLException
                        .throwAtomikosSQLException("The class '"
                                + getXaDataSourceClassName()
                                + "' specified by property 'xaDataSourceClassName' does not implement the required interface javax.jdbc.XADataSource. Please make sure the spelling is correct, and check your JDBC driver vendor's documentation.");
            }
            setXaDataSource((XADataSource) driver);
            getXaDataSource().setLoginTimeout(getLoginTimeout());
            getXaDataSource().setLogWriter(getLogWriter());
            PropertyUtils.setProperties(getXaDataSource(), getXaProperties());
        }

        JdbcTransactionalResource tr = new JdbcTransactionalResource(
                getUniqueResourceName(), getXaDataSource());
        com.atomikos.datasource.pool.ConnectionFactory cf = new com.atomikos.jdbc.AtomikosXAConnectionFactory(
                getXaDataSource(), tr, this);
        Configuration.removeResource(getUniqueResourceName());
        Configuration.addResource(tr);

        return cf;
    }
}
brian relph Send private email
Monday, December 21, 2009
 
 
This is great stuff!

Suppose we want to integrate this into our standard code base - what would be your recommendation?

We could try to incorporate this in our standard data source, but I would be interested in knowing your thoughts first.

Thanks
Guy
Guy Pardon Send private email
Monday, December 21, 2009
 
 
You are welcome to take the posted code and use it.  But, I think it would be better to have use a global configuration for UserTransactionServiceImp, like "com.atomikos.icatch.dynamic_resources", which defaults to false (to preserve existing behavior), but when set to "true", would allow re-binding of resources.

You could also offer other settings, like "safe" and/or "type-safe", where atomikos would only allow rebinds of objects that are equal, or objects of the same type.
brian relph Send private email
Monday, December 21, 2009
 
 
OK thanks, this makes perfect sense!

FYI your suggested functionality has been scheduled (for now at least) for our 3.6.1 release due end of January 2010.

We will contact you in due time to settle on the technical details...

Cheers
Guy Pardon Send private email
Monday, December 21, 2009
 
 

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics