在Weld中使用@EJB作为注入注释

时间:2011-11-29 13:21:12

标签: java cdi jboss-weld

我有一个应用程序,它是JavaEE(服务器端)部分JavaSE(客户端)的一部分。由于我希望客户端能够很好地构建,我使用Weld来注入各种组件。其中一些组件应该是服务器端的@EJB。

我打算做的是扩展Weld架构以提供“组件”,允许Weld在客户端尝试引用它们时执行JNDI查找以加载EJB实例。但是我该怎么做?

在其他问题中,我想要

在客户端

public class ClientCode {
    public @Inject @EJB MyEJBInterface;
}

在服务器端

@Stateless
public class MyEJB implements MyEJBInterface {
}

当创建ClientCode对象时,Weld“隐含地”执行JNDI查找。我怎么能这样做?

2 个答案:

答案 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注释,它没有方法,也没有常量,以使它尽可能简单。

构造JNDI客户端bean

这里有两个概念合并。

  • 一方面,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中。