вторник, 17 октября 2006 г.

JCA

Допустим мы пишем JCA RA который поддерживает исходящие соединения к EIS. Все написали. Верифицировали. Продеплоили в GlassFish. Все OK. Теперь продеплоили какое-то приложение, чтобы протестировать соединение к EIS. Запускаем. А оно нам @#%!: "Exception in NamingManagerImpl copyMutableObject() ... java.io.NotSerializableException".
Покопавшись мы выясняем, что падение происходит при копировании нашей имплементации java.resource.cci.ConnectionFactory интерфейса. А имеено в нашем классе ConnectionFactoryImpl есть не Serealizable поле, например ManagedConnectionFactory (содержащая сокет например). Убив N часов времени на поиски в гугле, выясняем, что GlassFish в JNDI помещает копии объектов! Наматерившись и решив, что разработчикам JNDI реализации виднее, мы делаем неугодное в ConnectionFactoryImpl поле transient'ым. А вдруг этот объект будет использоваться, только чтобы getReference вызвать? Хрен там. Повозившись еще, оказывается, что dependency injection отдает нам именно копию из JNDI у которой наше transient поле установленно в NULL!

Даём GlassFish'у последний шанс. Читаем его исходники и оказывается, что не все объекты помещаются в JNDI в сереализованном виде. Так например Connector'ы должны помещаться не сереализованными! Хм, а как же оно определяет, что этот объект коннектор? А вот как:

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

Короче, чтобы не иметь лишний геморрой: ConnectionFactory экземпляры должны помещаться в субконтексте /eis/ ! Если теперь поискать в гугле по словам "eis subcontext" то находятся рекомендации, а не требования!.

Вообще, это частая ошибка, проявляется в виде NPE (null pointer exception) при вызове transient полей (activemq этим страдает). Мне кажется, в 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");
}

и вызывать этот метод перед использованием this.managedConnectionFactory внутри ConnectionFactoryImpl.

Комментариев нет:

Отправить комментарий