当前位置: 首页 > 知识库问答 >
问题:

CDI扩展自动解析从一个EAR到另一个EAR对远程EJB的调用

百里锋
2023-03-14

我试图从另一个EAR调用部署在EAR中的远程服务时遇到了问题。我没有为我的EJB指定任何名称,不管它们是@本地还是@远程,所以只需使用注释并通过@EJB注入它。

这就是我所拥有的:

>

  • 耳朵/

    • lib/任何lib jar(包括远程服务B的API jar)
    • 战争
    • 服务A调用远程服务B的ejb模块

    耳朵B/

    • lib/任何API lib jar

    其他信息:服务B实现@本地和@远程接口,服务A通过以下方式将服务B注入远程接口:

    @EJB private MyRemoteInterface remoteService;
    

    这种结构在jboss服务器上运行得非常好,但是在webphere(8.5.5.1)中,我必须将名称绑定到远程EJB上。如果不在两个EAR上添加绑定(我通过管理控制台直接做了,不必编辑ejb-jar.xml),那么我的远程bean在运行时不会解决。当然,我必须使它的工作与是,否则我不会不张贴:)

    我的问题是:被迫用WebSphere命名远程EJB是正常的,还是一种回归(来自任何以前的版本)?我希望远程bean上的注入@EJB能够自动解析类型,但也许我在某个地方错了?

    解决方案:因为必须进行查找才能使解析工作正常,所以我决定将查找配置部分添加到客户机EJBJAR中。xml文件。这是由maven插件执行自动完成的,查找名称基于远程接口全名(包括包),因为如果EJB实现中未指定任何内容,这是WebSphere使用的默认绑定。

    我选择这个解决方案有两个原因:

    • 我不想在我的代码中查找(没有兴趣的重复代码)

    谢谢bkail的回答。

  • 共有2个答案

    宇文德明
    2023-03-14

    这与WebSphere Application Server的预期工作一致,并且不是回归。javadoc只需要在类型位于同一应用程序内时自动绑定@EJB

    如果没有提供显式链接信息,并且同一应用程序中只有一个会话bean公开了匹配的客户端视图类型,默认情况下,EJB依赖项解析为该会话bean。

    宫元徽
    2023-03-14

    最后,出于业务延迟的原因,我编写了一个CDI扩展来完成这项工作。扩展使用远程合约扫描所有注入点并代理它们。代理是按需创建的@ApplicationScoped托管bean,它们的工作仅包括:

    • 查找与扫描的远程合同相关的目标bean

    这个解决方案也为我提供了通过配置ENV变量在不同机器上处理查找操作的可能性,这样通过容器(即Docker)部署就可以很容易地工作(这是我们未来的目标之一)

    编辑:CDI扩展代码如下

    xtension.java:

    public class RemoteEjbExtension implements Extension {
    
    /**
     * This method is fired by the container for every Java EE component class
     * supporting injection that may be instantiated by the container at runtime,
     * including every managed bean declared using javax.annotation.ManagedBean,
     * EJB session or message-driven-bean, enabled bean, enabled interceptor or
     * enabled decorator.
     *
     * @param pit the event that has been fired
     */
    <T> void processInjectionTarget(@Observes final ProcessInjectionTarget<T> pit) {
        for (AnnotatedField<? super T> field : pit.getAnnotatedType().getFields()) {
            if (field.getJavaMember().getType().isAnnotationPresent(Remote.class)) {
                RemoteProxyFactory.putIfAbsent(field.getJavaMember().getType());
            }
        }
    }
    
    /**
     * This method is fired by the container when it has fully completed the
     * bean discovery process, validated that there are no definition errors
     * relating to the discovered beans, and registered Bean and ObserverMethod
     * objects for the discovered beans, but before detecting deployment problems.
     *
     * @param abd AfterBeanDiscovery fired events
     * @param bm Allows a portable extension to interact directly with the container.
     *          Provides operations for obtaining contextual references for beans,
     *          along with many other operations of use to portable extensions.
     */
    @SuppressWarnings("unchecked")
    void afterBeanDiscovery(@Observes final AfterBeanDiscovery abd, final BeanManager bm) {
    
        // Roll over discovered remote interfaces
        for (final Entry<String, Class<?>> remoteClassEntry : RemoteProxyFactory.getProxyClassEntries()) {
    
            // Proxy that points to the remote EJB
            final Object remoteProxy;
            final Class<?> remoteClass = remoteClassEntry.getValue();
    
            try {
                // Build a proxy that fetches the remote EJB using JNDI
                // and delegate the call.
                remoteProxy = RemoteProxyFactory.Builder.createEJBRemoteProxy(remoteClass);
            } catch (Exception e) {
                throw new IllegalStateException("Proxy creation for " + remoteClass.getCanonicalName() + " failed.", e);
            }
    
            final InjectionTarget<Object> it;
            try {
                AnnotatedType<Object> at = ((AnnotatedType<Object>) bm.createAnnotatedType(remoteProxy.getClass()));
                it = bm.createInjectionTarget(at);
            } catch (Exception e) {
                throw new IllegalStateException("Injection target for " + remoteClass.getCanonicalName() + " is invalid.", e);
            }
    
            final Bean<?> beanRemoteProxy = RemoteProxyFactory.Builder.createBeanForProxy(remoteProxy, it, remoteClass, ApplicationScoped.class);
            abd.addBean(beanRemoteProxy);
        }
    
    }
    }
    

    远程代理工厂。爪哇:

    public final class RemoteProxyFactory {
    
    /** The JNDI initial context */
    private static InitialContext CTX;
    static {
        try {
            RemoteProxyFactory.CTX = new InitialContext();
        } catch (NamingException e) {
            throw new IllegalStateException("Unable to get initial context.", e);
        }
    }
    
    private static final Map<String, Class<?>> REMOTE_EJB_CLASS_MAP = new ConcurrentHashMap<String, Class<?>>();
    
    /**
     * Register given class into proxy map
     * @param remoteEJBContractClass the remote contract's class to register
     */
    public static void putIfAbsent(final Class<?> remoteEJBContractClass) {
        // Works only for same class-loader. You would change this code
        // and transform the map to handle multiple class-loader for same contract.
        // In our current configuration there is no need as APIs / IMPL libraries share the same CL.
        if (!REMOTE_EJB_CLASS_MAP.containsKey(remoteEJBContractClass.getSimpleName())) {
            REMOTE_EJB_CLASS_MAP.put(remoteEJBContractClass.getSimpleName(), remoteEJBContractClass);
        }
    }
    
    public static Set<Entry<String, Class<?>>> getProxyClassEntries() {
        return REMOTE_EJB_CLASS_MAP.entrySet();
    }
    
    public static InitialContext getContext() {
        return RemoteProxyFactory.CTX;
    }
    
    public static final class Builder {
    
        private static final Logger LOGGER = Logger.getLogger(Builder.class.getName());
    
        /**
         * Create a new proxy that lookup the remote EJB
         * though JNDI.
         * @param remoteEJBClazz the remote class contract
         * @return a new remote EJB proxy
         */
        public static Object createEJBRemoteProxy(final Class<?> remoteEJBClazz) {
            return Proxy.newProxyInstance(remoteEJBClazz.getClassLoader(), new Class[] {
                remoteEJBClazz
            }, new InvocationHandler() {
    
                @Override
                public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
                    Object ejbInstance = null;
                    try {
                        // Pull the remote EJB from the JNDI
                        ejbInstance = RemoteProxyFactory.getContext().lookup(remoteEJBClazz.getName());
                    } catch (Exception e) {
                        throw new IllegalStateException("Remote EJB not found : " + remoteEJBClazz.getSimpleName(), e);
                    }
                    // Delegates the call to the remote EJB
                    return method.invoke(ejbInstance, args);
                }
            });
        }
    
        /**
         * Create a bean for given proxy / injection target / type / scope
         * @param proxy the proxy object
         * @param it the injection target
         * @param clazz the proxy type
         * @param targetScope the returned managed bean' scope
         * @return the managed bean handling given proxy
         */
        public static <T extends Object> Bean<T> createBeanForProxy(final T proxy, final InjectionTarget<T> it, final Class<?> clazz, final Class<? extends Annotation> targetScope) {
            return new Bean<T>() {
    
                @Override
                public T create(final CreationalContext<T> ctx) {
                    return proxy;
                }
    
                @Override
                public void destroy(final T instance, final CreationalContext<T> ctx) {
                    it.preDestroy(instance);
                    it.dispose(instance);
                    ctx.release();
                }
    
                @Override
                public Class<?> getBeanClass() {
                    return clazz;
                }
    
                @Override
                public Set<InjectionPoint> getInjectionPoints() {
                    return it.getInjectionPoints();
                }
    
                @Override
                public String getName() {
                    return clazz.toString();
                }
    
                @Override
                public Set<Annotation> getQualifiers() {
                    Set<Annotation> qualifiers = new HashSet<Annotation>();
                    qualifiers.add(new AnnotationLiteral<Default>() {
                        /** Default serial-id. */
                        private static final long serialVersionUID = 1L;
                    });
                    qualifiers.add(new AnnotationLiteral<Any>() {
                        /** Default serial-id. */
                        private static final long serialVersionUID = 1L;
                    });
                    return qualifiers;
                }
    
                @Override
                public Class<? extends Annotation> getScope() {
                    return targetScope;
                }
    
                @Override
                public Set<Class<? extends Annotation>> getStereotypes() {
                    return Collections.emptySet();
                }
    
                @Override
                public Set<Type> getTypes() {
                    Set<Type> types = new HashSet<Type>();
                    types.add(clazz);
                    return types;
                }
    
                @Override
                public boolean isAlternative() {
                    return false;
                }
    
                @Override
                public boolean isNullable() {
                    return false;
                }
    
            };
        }
    }
    

    }

     类似资料:
    • 基本上,我有一种情况,我必须在一个耳朵内访问JAR中声明的EJB,而不是在另一个耳朵中访问WAR。两个EAR都部署到同一个WebLogic托管服务器(同一个域)。Java代码中没有用于该EJB的注释。在ejbjar中。xml它被定义为会话无状态容器bean。在WebLogicEJBJAR中。xml指定了本地jndi名称。我尝试使用InitialContext查找这个bean,并从该接口获取Loca

    • 我目前的问题是,我的机器上运行了两个Wildfly 8.2.0最终实例。我知道,有类似的问题,但没有一个真正有助于我的问题。其中一个拥有一个宁静的应用程序,当它收到GET时,它会触发无状态会话Bean。之后,此无状态会话 Bean 应从远程无状态会话 Bean 调用方法该方法位于另一个 wildfly 实例上。 我将从解释我到目前为止所做的事情开始(也许我错过了一些东西,我对Java EE和Wil

    • 我正在使用Glassfish 4。我有一个耳朵,它有一个战争和一个罐子(与EJB)。 我想从WAR中调用EJB,但不确定是否需要使用本地或远程接口。 在我的罐子里,我的豆子是这样的: 在我的WAR中,我有一个web服务,它看起来如下所示: 这两个都包装成一个耳朵。 当我调用WebService方法时,我得到一个AccessLocalException: 首先: 这是调用EJB的正确方法吗。EAR中

    • 我不能从另一个ejb模块注入远程ejb。我把应用程序分成一个库和两个ejb模块。我尝试通过远程接口从一个ejb模块访问另一个模块,并获得javax.naming.NameNotFoundExc0019。我尝试从NewBean"@EJB私有CountryFacadeRemote"访问。我用了玻璃鱼。 我必须配置一些东西?谢谢。 下载源代码 MyAppTestEJB域名库: *国家: *Country

    • 我有一个ear,它包含2个war文件,每个war都包含无状态ejb和rest类。接口位于commons中。jar文件。耳朵结构如下所示: 我试图使用无状态-ejb-2中的无状态-ejb-1和注释,但我在部署期间遇到了错误。当我在stateless-EJB-2中使用@EJB时,就部署了ear,但在调用jersey-rest-2时,我遇到了一个远程查找错误。 这是我的方法调用链: 泽西-rest-1

    • 问题内容: 我正在为我的组织设计Jenkins CICD管道,但我有以下问题。 我来自一个devops团队,该团队控制着多个开发团队的Jenkins渠道。我基本上想编写一个可以由多个团队运行的具有多个阶段的Jenkins文件。我知道可以将此Jenkins文件检入每个团队的Gitrepo,并且一旦对代码存储库进行更改,它就可以调用完整的管道。 为确保此JenkinsFile是可维护的并且对于此Jen