Tuesday, October 17, 2006

JCA

Assume that we write an JCA RA supporting outbound connections to an EIS. We have written everything. It has been verificated and deployed into GlassFish. Everything is ok. Now we have deployed some application to test connection to the EIS. We execute it and see how it fails with: "Exception in NamingManagerImpl copyMutableObject() ... java.io.NotSerializableException".

After digging, we find out, that the exception throws on copying of implementation of the java.resource.cci.ConnectionFactory interface. And exactly, there is a non-serializable field in the ConnectionFactoryImpl, (the field is) ManagedConnectionFactory for example. After N hours have bean spended/killed for search in the google, we find out, that the GlassFish puts an object copy in JNDI - not the object itself! And again, after we have blamed and decided that JNDI implementators know more, we mark the disagreeable field in the ConnectionFactoryImpl as transient. Suppose this object will be used just to invoke getReference method? No: after we have bothered with it a bit more, it turns out, that dependency injection mechanism gives us the copy of the object from the JNDI with transient fields set to NULL or zero.

Lets give GlassFish a last chance. We read its sources, and find out that not every object goes through JNDI serialized. For example, Connectors should be thrown in not serialized! Hm, but how does it find that object is a Connector? In this way:

private boolean isConnector(String logicalJndiName){
return (logicalJndiName.indexOf(EIS_STRING)!=-1);
}
where EIS_STRING = "/eis/".


In short, remember it to eliminate extra troubles: ConnectionFactory instances must be placed in subcontext /eis/ !!

In general, this problem appears in a form of NPE (null pointer exception) when method is invoked on a transient field. I think, it is a good idea to have the following code in connectionFactoryImpl:

/**
* Ensure that managedConnectionFactory is not null.
* @throws ResourceException if managedConnectionFactory is null.
*/
protected void ensureManagedConnectionFactory () throws ResourceException {
if (this.managedConnectionFactory == null) {
throw new ResourceException (
"No reference to managed connection factory exists."
+ " Either it is a bug of the RA or your JNDI resource"
+ " is not in the /eis/ subcontext");
}

Always invoke this method before use of this.managedConnectionFactory.