簡體   English   中英

CDI擴展可自動解決從一個EAR到另一個EAR的對遠程EJB的調用

[英]CDI Extension to automatically resolve call to Remote EJB from one EAR to another EAR

嘗試從另一個EAR調用部署在EAR中的遠程服務時遇到問題。 我沒有為EJB指定任何名稱,無論它們是@Local還是@Remote,因此只需使用批注並通過@EJB注入即可。

這就是我所擁有的:

  • EAR A /

    • lib /任何lib jar(包括用於遠程服務B的API jar)
    • 戰爭
    • 具有服務A的ejb模塊調用遠程服務B
  • 耳B /

    • lib /任何API lib jar
    • 帶有服務B的ejb模塊

附加信息 :服務B同時實現@Local和@Remote接口,服務A通過以下方式通過遠程接口注入服務B:

@EJB private MyRemoteInterface remoteService;

這種結構在jboss服務器上可以很好地工作,但是在websphere(8.5.5.1)中,我必須將名稱綁定到遠程EJB上。 如果不在兩個EAR上都添加綁定(我是通過管理控制台直接這樣做的,而不必編輯ejb-jar.xml),那么在運行時將無法解析我的遠程bean。 當然,我必須使其與WAS一起使用,否則我將不會發布:)

我的問題:強制使用WebSphere命名遠程EJB是正常的還是是回歸(從任何先前版本開始)? 我期望在遠程bean上注入@EJB可以對類型進行自動解析,但是也許我在某個地方錯了?

解決方案 :因為必須完成查找才能使解析工作,所以我決定將查找配置部分添加到客戶端ejb-jar.xml文件中。 這是通過maven插件執行自動完成的,查找名稱基於遠程接口全名(包括軟件包),因為如果EJB實現中未指定任何內容,則這是WebSphere的默認綁定。

我選擇此解決方案有兩個原因:

  • 我不想在我的代碼中進行查找(重復的代碼沒有興趣)
  • 我需要使它對其他開發人員來說是自動且透明的

謝謝bkail的回答。

對於WebSphere Application Server來說,這可以按預期工作,並且不是回歸。 僅當類型在同一應用程序內時, javadoc才需要自動綁定@EJB

如果未提供任何顯式鏈接信息,並且在同一應用程序中只有一個會話Bean公開匹配的客戶端視圖類型,則默認情況下,EJB依賴關系解析為該會話Bean。

最后,出於業務延誤的原因,我編寫了CDI擴展名以完成這項工作。 該擴展程序使用遠程合約掃描所有注入點並對其進行代理。 代理是按需創建的@ApplicationScoped托管Bean,它們的工作僅包括:

  • 查找與掃描的遠程合約相關的目標Bean
  • 委托執行被調用的遠程方法

該解決方案還為我提供了通過ENV變量通過配置來處理不同機器上的查找操作的可能性,從而可以輕松進行容器(即Docker)的部署(這是未來的目標之一)

編輯 :下面的CDI擴展代碼

RemoteEjbExtension.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);
    }

}
}

RemoteProxyFactory.java

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;
            }

        };
    }
}

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM