题
我有一个应用程序,该应用程序是Javaee(服务器端)部分Javase(客户端)。由于我希望该客户端进行良好的架构,因此我在其中使用焊接来注入各种组件。其中一些组件应为服务器端@EJB。
我计划要做的是扩展焊接体系结构,以提供“组件”,允许焊缝在客户端尝试参考时执行JNDI查找以加载EJB的实例。但是我该怎么做?
在其他忧虑中,我想拥有
在客户端
public class ClientCode {
public @Inject @EJB MyEJBInterface;
}
在服务器端
@Stateless
public class MyEJB implements MyEJBInterface {
}
当创建客户端代码对象时,使用焊缝“隐含”执行JNDI查找。我怎样才能做到这一点 ?
解决方案
基本上,这样做需要写一个所谓的 便携式CDI扩展.
但是,由于它很长,需要进行一些调整,因此让我进一步解释。
便携式扩展
像Weld Doc所说的那样,第一步是创建一个实现该类别的类 Extension
标记接口,其中将编写与有趣的CDI事件相对应的代码。在这种确切的情况下,最有趣的事件是,在我看来 Afterbeandiscovery. 。实际上,在CDI IMPHEN发现所有“局部”豆之后,发生了此事件。
因此,写入扩展名更少,为该事件编写处理程序:
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
没有任何方法的注释都不是常数,以使其尽可能简单。
构建JNDI客户端
两个概念在这里合并。
- 一方面
Bean
界面似乎(对我来说)定义了豆是什么。 - 在另一侧,
InjectionTarget
在一定的延伸中定义了那个非常豆的生命周期。
从我能够找到的文献中,这两个接口实现通常至少共享某些状态。因此,我决定使用独特的类推动他们: JndiBean
!
在该豆中,大多数方法都空为空(或默认值)
Bean#getTypes
, ,必须返回EJB远程接口和所有扩展@Remote
接口(可以通过此接口来调用这些接口的方法)Bean#getQualifiers
返回仅包含一个元素的集合:一个AnnotationLiteral
对应于@JndiClient
界面。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;
而且它没有任何问题
未来?
好吧,目前尚未管理用户凭据,但是我想使用 C CDI:上下文...哦,不!不是上下文: 范围 !
其他提示
CDI规格的第3.5节应提供帮助。您可能还需要使用EJB注释上的一些属性。另外,(可能不需要告诉您)确保在客户端上正确设置JNDI以引用服务器,并将任何所需的接口打包到客户端JAR中。