[英]CDI Extension to automatically resolve call to Remote EJB from one EAR to another EAR
嘗試從另一個EAR調用部署在EAR中的遠程服務時遇到問題。 我沒有為EJB指定任何名稱,無論它們是@Local還是@Remote,因此只需使用批注並通過@EJB注入即可。
這就是我所擁有的:
EAR A /
耳B /
附加信息 :服務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,它們的工作僅包括:
該解決方案還為我提供了通過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.