使用@EJB作为Weld中的注入注释 [英] Use @EJB as injection annotation in Weld
问题描述
我有一个应用程序,它是JavaEE(服务器端)和JavaSE(客户端)的一部分。由于我希望该客户端具有良好的体系结构,因此我在其中使用了Weld来注入各种组件。其中一些组件应该是服务器端@EJB。
I have an application which is part JavaEE (the server side) part JavaSE (the client side). As I want that client to be well architectured, I use Weld in it to inject various components. Some of these components should be server-side @EJB.
我打算做的是扩展Weld体系结构,以提供允许Weld执行JNDI查找的组件当客户端尝试引用EJB实例时,请加载它们。但是我该怎么做呢?
What I plan to do is to extend Weld architecture to provide the "component" allowing Weld to perform JNDI lookup to load instances of EJBs when client tries to reference them. But how do I do that ?
在其他方面,我想在客户端使用
In other worrds, I want to have
/ p>
on the client side
public class ClientCode {
public @Inject @EJB MyEJBInterface;
}
在服务器端
@Stateless
public class MyEJB implements MyEJBInterface {
}
在创建ClientCode对象时,使用Weld隐式执行JNDI查找。我该怎么做?
With Weld "implicitely" performing the JNDI lookup when ClientCode objects are created. How can I do that ?
推荐答案
基本上,这样做需要编写所谓的便携式CDI扩展。
Basically, doing so requires write a so-called portable CDI extension.
但是,由于它很长并且需要一些调整,让我进一步解释。
But, as it is quite long and requires a few tweaks, let me explain it further.
就像焊接文档解释的那样,第一步是创建一个实现 扩展
标记接口,在其中将编写与有趣的CDI事件相对应的代码。在这种精确的情况下,在我看来,最有趣的事件是 AfterBeanDiscovery 。确实,此事件发生在CDI impl找到了所有本地 bean之后。
Like weld doc explains, first step is to create a class that implements the Extension
tagging interface, in which one will write code corresponding to interesting CDI events. In that precise case, the most interesting event is, to my mind, AfterBeanDiscovery. Indeed, this event occurs after all "local" beans have been found by CDI impl.
因此,编写扩展名的操作更少,为该事件编写处理程序:
So, writing extension is, more opr less, writing a handler for that event :
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反射对象转换为更高级的焊接对象(在我的情况下)
Creating the bean is not a trivial operation : it requires transforming "basic" Java reflection objects into more advanced weld ones (well, in my case)
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类。但是在此之前,需要在注释领域中进行少量旅行。
Finally, one has to write the JndiBean class. But before, a small travel in annotations realm is required.
首先,我使用 @EJB
一个。一个坏的想法:Weld使用限定符注释方法调用result来构建bean的哈希码!因此,我创建了自己的 @JndiClient
批注,该批注不包含任何方法,也不包含任何常量,以使其尽可能简单。
At first, I used the @EJB
one. A bad idea : Weld uses qualifier annotation method calls result to build hashcode of bean ! So, I created my very own @JndiClient
annotation, which holds no methods, neither constants, in order for it to be as simple as possible.
两个概念在这里合并。
Two notions merge here.
- 一方面,
Bean
接口(对我而言)似乎可以定义Bean是什么。 - 另一方面,
InjectionTarget
在一定程度上定义了该bean的生命周期。
- On one side, the
Bean
interface seems (to me) to define what the bean is. - On the other side, the
InjectionTarget
defines, to a certain extend, the lifecycle of that very bean.
从我能够找到的文献中,这两个接口实现通常至少共享一些状态。因此,我决定使用一个唯一的类来推动它们: JndiBean
!
From the literature I was able to find, those two interfaces implementations often share at least some of their state. So I've decided to impelment them using a unique class : the JndiBean
!
在该bean中,大多数
In that bean, most of the methods are left empty (or to a default value) excepted
-
Bean#getTypes
,它必须返回EJB远程接口和所有扩展的@Remote
接口(因为可以通过此接口调用这些接口中的方法) -
Bean#getQualifiers
返回仅包含一个元素的Set:一个AnnotationLiteral
对应于@JndiClient
接口。 -
Contextual#create
(您忘了Bean扩展了Contextual,不是吗?),它执行查找:
Bean#getTypes
, which must return the EJB remote interface and all extended@Remote
interfaces (as methods from these interfaces can be called through this interface)Bean#getQualifiers
which returns a Set containing only one element : anAnnotationLiteral
corresponding to@JndiClient
interface.Contextual#create
(you forgot Bean extended Contextual, didn't you ?) which performs the lookup :
@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客户端代码中,我可以编写
Well, now, in my glassfish java client code, I can write things such as
private @Inject @JndiClient MyRemoteEJB instance;
它可以正常工作
现在,用户凭据尚未得到管理,但我想使用CDI的 C 是完全有可能的:上下文...哦,不!不是上下文:范围!
Well, for now, user credentials are not managed, but I guess it could be totally possible using the C of CDI : Contexts ... oh no ! Not contexts : scopes !
这篇关于使用@EJB作为Weld中的注入注释的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!