我有一个应用程序,它是JavaEE(服务器端)部分JavaSE(客户端)的一部分。由于我希望客户端能够很好地构建,我使用Weld来注入各种组件。其中一些组件应该是服务器端的@EJB。
我打算做的是扩展Weld架构以提供“组件”,允许Weld在客户端尝试引用它们时执行JNDI查找以加载EJB实例。但是我该怎么做?
在其他问题中,我想要
在客户端
public class ClientCode {
public @Inject @EJB MyEJBInterface;
}
在服务器端
@Stateless
public class MyEJB implements MyEJBInterface {
}
当创建ClientCode对象时,Weld“隐含地”执行JNDI查找。我怎么能这样做?
答案 0 :(得分:3)
基本上,这样做需要写一个所谓的portable CDI extension。
但是,因为它很长并需要一些调整,让我进一步解释。
就像焊接文档解释的那样,第一步是创建一个实现Extension
标记接口的类,其中一个人将编写与有趣的CDI事件相对应的代码。在那个精确的案例中,我认为最有趣的事件是AfterBeanDiscovery。实际上,这个事件发生在CDI impl发现所有“本地”bean之后。
因此,编写扩展名更为可能,为该事件编写处理程序:
public void loadJndiBeansFromServer(
@Observes AfterBeanDiscovery beanDiscovery, BeanManager beanManager)
throws NamingException, ClassNotFoundException, IOException {
// Due to my inability to navigate in server JNDI naming (a weird issue in Glassfish naming)
// This props maps interface class to JNDI name for its server-side
Properties interfacesToNames = extractInterfacesToNames();
// JNDI properties
Properties jndiProperties = new Properties();
Context context = new InitialContext();
for (Entry<?, ?> entry : interfacesToNames.entrySet()) {
String interfaceName = entry.getKey().toString();
Class<?> interfaceClass = Class.forName(interfaceName);
String jndiName = entry.getValue().toString();
Bean<?> jndiBean = createJndIBeanFor(beanManager, interfaceClass, jndiName, jndiProperties);
beanDiscovery.addBean(jndiBean);
}
}
创建bean不是一个简单的操作:它需要将“基本”Java反射对象转换为更高级的焊接对象(在我的情况下)(
)private <Type> Bean<Type> createJndIBeanFor(BeanManager beanManager, Class<Type> interfaceClass,
String jndiName, Properties p) {
AnnotatedType<Type> annotatedType = beanManager
.createAnnotatedType(interfaceClass);
// Creating injection target in a classical way will fail, as interfaceClass is the interface of an EJB
JndiBean<Type> beanToAdd = new JndiBean<Type>(interfaceClass, jndiName, p);
return beanToAdd;
}
最后,必须编写JndiBean类。但之前,需要在注释领域进行小规模旅行。
起初,我使用了@EJB
。一个糟糕的想法:Weld使用限定符注释方法调用结果来构建bean的哈希码!所以,我创建了我自己的@JndiClient
注释,它没有方法,也没有常量,以使它尽可能简单。
这里有两个概念合并。
Bean
接口似乎(对我而言)定义了bean的内容。InjectionTarget
在某种程度上定义了那个bean的生命周期。从我能够找到的文献中,这两个接口实现通常至少共享一些状态。所以我决定使用一个独特的类来强加它们:JndiBean
!
在该bean中,除了
之外,大多数方法都是空的(或默认值)Bean#getTypes
,必须返回EJB远程接口和所有扩展@Remote
接口(可以通过此接口调用这些接口中的方法)Bean#getQualifiers
返回仅包含一个元素的Set:与@JndiClient
接口对应的AnnotationLiteral
。 Contextual#create
(你忘记了Bean扩展上下文,不是吗?)执行查找:
@Override
public T create(CreationalContext<T> arg0) {
// Some classloading confusion occurs here in my case, but I guess they're of no interest to you
try {
Hashtable contextProps = new Hashtable();
contextProps.putAll(jndiProperties);
Context context = new InitialContext(contextProps);
Object serverSide = context.lookup(jndiName);
return interfaceClass.cast(serverSide);
} catch (NamingException e) {
// An unchecked exception to go through weld and break the world appart
throw new LookupFailed(e);
}
}
这就是全部
好吧,现在,在我的glassfish java客户端代码中,我可以编写诸如
之类的东西private @Inject @JndiClient MyRemoteEJB instance;
它没有任何问题
嗯,目前用户凭据不受管理,但我想完全有可能使用CDI的 C :上下文......哦不!不是上下文:scopes!
答案 1 :(得分:1)
CDI规范的第3.5节应该有所帮助。您可能还想使用EJB注释上的一些属性。另外,(可能不需要告诉你)确保在客户端上正确设置JNDI以引用服务器,并将任何所需的接口打包到客户端jar中。