blob: 055afbc0db7ef04f81bbb01356441cab9b302b93 [file] [log] [blame]
/*
* 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) {
super.fireEvent(event, metadata, isLifecycleEvent);
if (isEvent(event)) {
final BeanManagerImpl parentBm = getParentBm();
if (parentBm != null) {
parentBm.fireEvent(event, metadata, isLifecycleEvent);
}
}
}
@Override
public List<Interceptor<?>> resolveInterceptors(InterceptionType type, 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<>(super.resolveObserverMethods(event, metadata));
if (isEvent(event)) {
final BeanManagerImpl parentBm = getParentBm();
if (parentBm != null) {
set.addAll(parentBm.resolveObserverMethods(event, metadata));
}
} // 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(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(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;
}
}
}