gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-02 05:13:04 28 4
gpt4 key购买 nike

我在尝试从另一个 EAR 调用部署在一个 EAR 中的远程服务时遇到问题。我没有为我的 EJB 指定任何名称,无论它们是 @Local 还是 @Remote,因此只需使用注释并通过 @EJB 注入(inject)它。

这就是我所拥有的:

  • EAR A/

    • lib/任何lib jar(包括远程服务B的API jar)
    • war
    • 带有服务 A 调用远程服务 B 的 ejb 模块
  • EAR B/

    • lib/任何 API lib jar
    • 带有服务 B 的 ejb 模块

附加信息:服务 B 实现 @Local 和 @Remote 接口(interface),服务 A 通过 Remote 接口(interface)注入(inject)服务 B:

@EJB private MyRemoteInterface remoteService;

此结构与 jboss 服务器完美配合,但对于 websphere (8.5.5.1),我必须将名称绑定(bind)到我的远程 EJB。如果不在两个 EAR 上添加绑定(bind)(我直接通过管理控制台执行此操作,不必编辑 ejb-jar.xml),那么我的远程 bean 在运行时不会解析。当然,我必须让它与WAS一起工作,否则我不会不发帖:)

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

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

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

  • 我不想在我的代码中进行查找(没有兴趣的重复代码)
  • 我需要使其对其他开发者来说自动化且透明

感谢 bkail 的回答。

最佳答案

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

  • 查找与扫描的远程合约相关的目标bean
  • 委托(delegate)执行被调用的远程方法

这个解决方案还为我提供了通过 ENV 变量配置来处理不同机器上的查找操作的可能性,以便容器(即 Docker)的部署可以轻松工作(这是我们 future 的目标之一)

编辑:下面的 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;
}

};
}
}

}

关于java - CDI 扩展自动解析从一个 EAR 到另一个 EAR 对远程 EJB 的调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27188113/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com