/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.openejb.cdi; import org.apache.openejb.util.reflection.Reflections; import org.apache.webbeans.component.BuiltInOwbBean; import org.apache.webbeans.component.ExtensionBean; import org.apache.webbeans.component.OwbBean; import org.apache.webbeans.config.WebBeansContext; import org.apache.webbeans.container.BeanManagerImpl; import org.apache.webbeans.context.creational.CreationalContextImpl; import org.apache.webbeans.event.EventMetadataImpl; import org.apache.webbeans.util.Asserts; import org.apache.webbeans.util.WebBeansUtil; import javax.el.ELResolver; import javax.el.ExpressionFactory; import javax.enterprise.context.spi.Context; import javax.enterprise.context.spi.Contextual; import javax.enterprise.context.spi.CreationalContext; import javax.enterprise.inject.UnsatisfiedResolutionException; import javax.enterprise.inject.spi.AnnotatedType; import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.InjectionPoint; import javax.enterprise.inject.spi.InjectionTarget; import javax.enterprise.inject.spi.InterceptionType; import javax.enterprise.inject.spi.Interceptor; import javax.enterprise.inject.spi.ObserverMethod; import javax.enterprise.inject.spi.PassivationCapable; import java.lang.annotation.Annotation; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; public class WebappBeanManager extends BeanManagerImpl { private static final Annotation[] EMPTY_ANNOTATIONS = new Annotation[0]; private static final ThreadLocal<Boolean> USE_PARENT_BM = new ThreadLocal<>(); private final WebappWebBeansContext webappCtx; private final InheritedBeanFilter filter; private Set<Bean<?>> deploymentBeans; private boolean started/* = false*/; public WebappBeanManager(final WebappWebBeansContext ctx) { super(ctx); webappCtx = ctx; deploymentBeans = super.getBeans(); // use the parent one while starting Reflections.set(this, "injectionResolver", new WebAppInjectionResolver(ctx)); filter = new InheritedBeanFilter(this); } @Override public void fireEvent(final Object event, final EventMetadataImpl metadata, final boolean isLifecycleEvent) { getNotificationManager().fireEvent(event, metadata, isLifecycleEvent); if (isEvent(event)) { final BeanManagerImpl parentBm = getParentBm(); if (parentBm != null) { parentBm.getNotificationManager().fireEvent(event, metadata, isLifecycleEvent); } } } @Override public List<Interceptor<?>> resolveInterceptors(final InterceptionType type, final Annotation... interceptorBindings) { final List<Interceptor<?>> interceptors = super.resolveInterceptors(type, interceptorBindings); final List<Interceptor<?>> parentInterceptors = getParentBm().resolveInterceptors(type, interceptorBindings); for (final Interceptor<?> i : parentInterceptors) { if (!interceptors.contains(i)) { interceptors.add(i); } } return interceptors; } @Override public <T> Set<ObserverMethod<? super T>> resolveObserverMethods(final T event, final EventMetadataImpl metadata) { final Set<ObserverMethod<? super T>> set = new HashSet<>(); set.addAll(getNotificationManager().resolveObservers(event, metadata, false)); if (isEvent(event)) { final BeanManagerImpl parentBm = getParentBm(); if (parentBm != null) { set.addAll(parentBm.getNotificationManager().resolveObservers(event, metadata, false)); } } // else nothing since extensions are loaded by classloader so we already have it return set; } @Override public Object getInjectableReference(final InjectionPoint injectionPoint, final CreationalContext<?> ctx) { Asserts.assertNotNull(injectionPoint, "injectionPoint parameter"); if(injectionPoint == null) { return null; } final BeanManagerImpl parentBm = getParentBm(); final Boolean existing = USE_PARENT_BM.get(); if (existing != null && existing) { // shortcut the whole logic to keep the threadlocal set up correctly if (parentBm == null) { return null; } return parentBm.getInjectableReference(injectionPoint, ctx); } // we can do it cause there is caching but we shouldn't - easy way to overide OWB actually final Bean<Object> injectedBean = (Bean<Object>)getInjectionResolver().getInjectionPointBean(injectionPoint); try { if (parentBm != null && injectedBean != null && injectedBean == parentBm.getInjectionResolver().getInjectionPointBean(injectionPoint)) { USE_PARENT_BM.set(true); try { return parentBm.getInjectableReference(injectionPoint, ctx); } finally { USE_PARENT_BM.remove(); } } } catch (final UnsatisfiedResolutionException ure) { // skip, use this bean } return super.getInjectableReference(injectionPoint, ctx); } @Override public <T> CreationalContextImpl<T> createCreationalContext(final Contextual<T> contextual) { try { return super.createCreationalContext(contextual); } catch (final RuntimeException e) { // can happen? try { return getParentBm().createCreationalContext(contextual); } catch (final RuntimeException ignored) { throw e; } } } @Override public boolean isNormalScope(final Class<? extends Annotation> annotationType) { try { return super.isNormalScope(annotationType); } catch (final RuntimeException e) { try { return getParentBm().isNormalScope(annotationType); } catch (final RuntimeException ignored) { throw e; } } } @Override public boolean isPassivatingScope(final Class<? extends Annotation> annotationType) { try { return super.isPassivatingScope(annotationType); } catch (final RuntimeException e) { try { return getParentBm().isPassivatingScope(annotationType); } catch (final RuntimeException ignored) { throw e; } } } @Override public boolean isQualifier(final Class<? extends Annotation> annotationType) { try { return super.isQualifier(annotationType); } catch (final RuntimeException e) { try { return getParentBm().isQualifier(annotationType); } catch (final RuntimeException ignored) { throw e; } } } @Override public boolean isInterceptorBinding(final Class<? extends Annotation> annotationType) { try { return super.isInterceptorBinding(annotationType); } catch (final RuntimeException e) { try { return getParentBm().isInterceptorBinding(annotationType); } catch (final RuntimeException ignored) { throw e; } } } @Override public boolean isStereotype(final Class<? extends Annotation> annotationType) { try { return super.isStereotype(annotationType); } catch (final RuntimeException e) { try { return getParentBm().isStereotype(annotationType); } catch (final RuntimeException ignored) { throw e; } } } @Override public Set<Annotation> getInterceptorBindingDefinition(final Class<? extends Annotation> qualifier) { try { return super.getInterceptorBindingDefinition(qualifier); } catch (final RuntimeException e) { try { return getParentBm().getInterceptorBindingDefinition(qualifier); } catch (final RuntimeException ignored) { throw e; } } } @Override public Context getContext(final Class<? extends Annotation> scope) { try { return super.getContext(scope); } catch (final RuntimeException e) { try { return getParentBm().getContext(scope); } catch (final RuntimeException ignored) { throw e; } } } @Override public ELResolver getELResolver() { return new WebAppElResolver(super.getELResolver(), getParentBm().getELResolver()); } @Override public <T> AnnotatedType<T> createAnnotatedType(final Class<T> type) { try { return super.createAnnotatedType(type); } catch (final RuntimeException e) { try { return getParentBm().createAnnotatedType(type); } catch (final RuntimeException ignored) { throw e; } } } @Override public <T> InjectionTarget<T> createInjectionTarget(final AnnotatedType<T> type) { try { return super.createInjectionTarget(type); } catch (final RuntimeException e) { try { return getParentBm().createInjectionTarget(type); } catch (final RuntimeException ignored) { throw e; } } } @Override public ExpressionFactory wrapExpressionFactory(final ExpressionFactory expressionFactory) { return super.wrapExpressionFactory(expressionFactory); } public BeanManagerImpl getParentBm() { final WebBeansContext parent = webappCtx.getParent(); return parent != null ? parent.getBeanManagerImpl() : null; } @Override public boolean isInUse() { return super.isInUse() || getParentBm().isInUse(); } @Override public Set<Bean<?>> getComponents() { if (!started) { // probably not yet merged (afterStart()) // so reuse parent beans // this can happen for validations return new IteratorSet<>( new MultipleIterator<>( filter, deploymentBeans.iterator(), getParentBm().getComponents().iterator())); } return deploymentBeans; } @Override public Set<Bean<?>> getBeans() { return deploymentBeans; } @Override public <T> void addAdditionalAnnotatedType(Object extension, AnnotatedType<T> inAnnotatedType, String id) { super.addAdditionalAnnotatedType(extension, inAnnotatedType, id); } @Override public Bean<?> getPassivationCapableBean(final String id) { final Bean<?> bean = super.getPassivationCapableBean(id); if (bean == null && getParentBm() != null) { return getParentBm().getPassivationCapableBean(id); } return bean; } public void afterStart() { started = true; deploymentBeans = mergeBeans(); webappCtx.getBeanManagerImpl().getInjectionResolver().clearCaches(); // to force new resolution with new beans } private Set<Bean<?>> mergeBeans() { final Set<Bean<?>> allBeans = new CopyOnWriteArraySet<>(); // override parent one with a "webapp" bean list final BeanManagerImpl parentBm = getParentBm(); if (parentBm != null) { for (final Bean<?> bean : parentBm.getBeans()) { if (filter.accept(bean)) { allBeans.add(bean); } } } allBeans.addAll(super.getBeans()); return allBeans; } public void beforeStop() { // no-op } private boolean isEvent(final Object event) { return !WebBeansUtil.isDefaultExtensionBeanEventType(event.getClass()) && !webappCtx.getWebBeansUtil().isContainerEventType(event); } private interface Filter<A> { boolean accept(A a); } private static final class InheritedBeanFilter implements Filter<Bean<?>> { private final WebappBeanManager beanManager; private InheritedBeanFilter(final WebappBeanManager beanManager) { this.beanManager = beanManager; } @Override public boolean accept(final Bean<?> bean) { if (BuiltInOwbBean.class.isInstance(bean) || ExtensionBean.class.isInstance(bean)) { return false; } if (OwbBean.class.isInstance(bean)) { final OwbBean owbBean = OwbBean.class.cast(bean); if (owbBean.isPassivationCapable()) { if (hasBean(owbBean.getId())) { return false; } } } else if (PassivationCapable.class.isInstance(bean)) { if (hasBean(PassivationCapable.class.cast(bean).getId())) { return false; } } final Set<Annotation> qualifiers = bean.getQualifiers(); return beanManager.getBeans( bean.getBeanClass(), qualifiers.isEmpty() ? EMPTY_ANNOTATIONS : qualifiers.toArray(new Annotation[qualifiers.size()])).isEmpty(); } private boolean hasBean(final String id) { return beanManager.getPassivationCapableBean(id) != null; } } private static final class MultipleIterator<A> implements Iterator<A> { private final Iterator<A>[] delegates; private final Filter<A> filter; private A next/* = null*/; private int idx/* = 0*/; /** * @param filter used to filter delegates from index 1 to N-1 (0 is not filtered) * @param delegates iterator this Iterator merges, one delegates is mandatory */ private MultipleIterator(final Filter<A> filter, final Iterator<A>... delegates) { this.filter = filter; this.delegates = delegates; } @Override public boolean hasNext() { for (; idx < delegates.length; idx++) { while (delegates[idx].hasNext()) { next = delegates[idx].next(); if (idx == 0 || filter.accept(next)) { // we accept all items of first iterator return true; } } } return false; } @Override public A next() { return next; } @Override public void remove() { delegates[idx].remove(); } } // hack set, only use it for Set which are used as Iterator // case of getComponent private static final class IteratorSet<A> extends HashSet<A> { private final Iterator<A> it; private IteratorSet(final Iterator<A> it) { this.it = it; } @Override public Iterator<A> iterator() { return it; } } }