All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jboss.weld.bean.builtin.InstanceImpl Maven / Gradle / Ivy

Go to download

This jar bundles all the bits of Weld and CDI required for running in a Servlet container.

There is a newer version: 6.0.0.Beta4
Show newest version
/*
 * JBoss, Home of Professional Open Source
 * Copyright 2008, Red Hat, Inc., and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * Licensed 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.jboss.weld.bean.builtin;

import static org.jboss.weld.util.Preconditions.checkNotNull;
import static org.jboss.weld.util.reflection.Reflections.cast;

import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.ref.WeakReference;
import java.lang.reflect.Type;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;

import javax.enterprise.context.Dependent;
import javax.enterprise.context.spi.AlterableContext;
import javax.enterprise.context.spi.Context;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.Instance;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.enterprise.util.TypeLiteral;

import org.jboss.weld.bean.proxy.ProxyMethodHandler;
import org.jboss.weld.bean.proxy.ProxyObject;
import org.jboss.weld.contexts.WeldCreationalContext;
import org.jboss.weld.exceptions.InvalidObjectException;
import org.jboss.weld.inject.WeldInstance;
import org.jboss.weld.injection.CurrentInjectionPoint;
import org.jboss.weld.injection.ThreadLocalStack.ThreadLocalStackReference;
import org.jboss.weld.logging.BeanLogger;
import org.jboss.weld.logging.BeanManagerLogger;
import org.jboss.weld.manager.BeanManagerImpl;
import org.jboss.weld.module.EjbSupport;
import org.jboss.weld.resolution.Resolvable;
import org.jboss.weld.resolution.ResolvableBuilder;
import org.jboss.weld.resolution.TypeSafeBeanResolver;
import org.jboss.weld.util.AnnotationApiAbstraction;
import org.jboss.weld.util.InjectionPoints;
import org.jboss.weld.util.LazyValueHolder;
import org.jboss.weld.util.collections.WeldCollections;
import org.jboss.weld.util.reflection.Formats;
import org.jboss.weld.util.reflection.Reflections;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

/**
 * Helper implementation for Instance for getting instances
 *
 * @param 
 * @author Gavin King
 */
@SuppressFBWarnings(value = { "SE_NO_SUITABLE_CONSTRUCTOR", "SE_BAD_FIELD" }, justification = "Uses SerializationProxy")
public class InstanceImpl extends AbstractFacade> implements WeldInstance, Serializable {

    private static final long serialVersionUID = -376721889693284887L;

    private final transient Set> allBeans;
    private final transient Bean bean;

    private final transient CurrentInjectionPoint currentInjectionPoint;
    private final transient InjectionPoint ip;
    private final transient EjbSupport ejbSupport;

    public static  Instance of(InjectionPoint injectionPoint, CreationalContext creationalContext, BeanManagerImpl beanManager) {
        return new InstanceImpl(injectionPoint, creationalContext, beanManager);
    }

    private InstanceImpl(InjectionPoint injectionPoint, CreationalContext creationalContext, BeanManagerImpl beanManager) {
        super(injectionPoint, creationalContext, beanManager);

        if (injectionPoint.getQualifiers().isEmpty() && Object.class.equals(getType())) {
            // Do not prefetch the beans for Instance with no qualifiers
            allBeans = null;
            bean = null;
        } else {
            this.allBeans = resolveBeans();
            // Optimization for the most common path - non-null bean means we are not unsatisfied not ambiguous
            if (allBeans.size() == 1) {
                this.bean = allBeans.iterator().next();
            } else {
                this.bean = null;
            }
        }

        this.currentInjectionPoint = beanManager.getServices().getRequired(CurrentInjectionPoint.class);
        // Generate a correct injection point for the bean, we do this by taking the original injection point and adjusting the
        // qualifiers and type
        this.ip = new DynamicLookupInjectionPoint(getInjectionPoint(), getType(), getQualifiers());
        this.ejbSupport = beanManager.getServices().get(EjbSupport.class);
    }

    public T get() {
        checkBeanResolved();
        return getBeanInstance(bean);
    }

    /**
     * Gets a string representation
     *
     * @return A string representation
     */
    @Override
    public String toString() {
        return Formats.formatAnnotations(getQualifiers()) + " Instance<" + Formats.formatType(getType()) + ">";
    }

    public Iterator iterator() {
        return new InstanceImplIterator(allBeans());
    }

    public boolean isAmbiguous() {
        return allBeans().size() > 1;
    }

    public boolean isUnsatisfied() {
        return allBeans().isEmpty();
    }

    public WeldInstance select(Annotation... qualifiers) {
        return selectInstance(this.getType(), qualifiers);
    }

    public  WeldInstance select(Class subtype, Annotation... qualifiers) {
        return selectInstance(subtype, qualifiers);
    }

    public  WeldInstance select(TypeLiteral subtype, Annotation... qualifiers) {
        return selectInstance(subtype.getType(), qualifiers);
    }

    @Override
    @SuppressWarnings("unchecked")
    public  WeldInstance select(Type subtype, Annotation... qualifiers) {
        // verify if this was invoked on WeldInstance
        if (!this.getType().equals(Object.class)) {
            throw BeanLogger.LOG.selectByTypeOnlyWorksOnObject();
        }
        // This cast should be safe, we make sure that this method is only invoked on WeldInstance
        // and any type X will always extend Object
        return (WeldInstance)selectInstance(subtype, qualifiers);
    }

    private  WeldInstance selectInstance(Type subtype, Annotation[] newQualifiers) {
        InjectionPoint modifiedInjectionPoint = new FacadeInjectionPoint(getBeanManager(), getInjectionPoint(), Instance.class, subtype, getQualifiers(),
                newQualifiers);
        return new InstanceImpl(modifiedInjectionPoint, getCreationalContext(), getBeanManager());
    }

    @Override
    public void destroy(T instance) {
        checkNotNull(instance);
        // Attempt to destroy instance which is either a client proxy or a dependent session bean proxy
        if (instance instanceof ProxyObject) {
            ProxyObject proxy = (ProxyObject) instance;
            if (proxy.weld_getHandler() instanceof ProxyMethodHandler) {
                ProxyMethodHandler handler = (ProxyMethodHandler) proxy.weld_getHandler();
                Bean bean = handler.getBean();
                if (isSessionBeanProxy(instance) && Dependent.class.equals(bean.getScope())) {
                    // Destroy internal reference to a dependent session bean
                    destroyDependentInstance(instance);
                    return;
                } else {
                    // Destroy contextual instance of a normal-scoped bean
                    Context context = getBeanManager().getContext(bean.getScope());
                    if (context instanceof AlterableContext) {
                        AlterableContext alterableContext = (AlterableContext) context;
                        alterableContext.destroy(bean);
                        return;
                    } else {
                        throw BeanLogger.LOG.destroyUnsupported(context);
                    }
                }
            }
        }
        // Attempt to destroy dependent instance which is neither a client proxy nor a dependent session bean proxy
        destroyDependentInstance(instance);
    }

    @Override
    public Handler getHandler() {
        checkBeanResolved();
        return new HandlerImpl(() -> getBeanInstance(bean), this, bean);
    }

    @Override
    public boolean isResolvable() {
        return allBeans().size() == 1;
    }

    @Override
    public Iterable> handlers() {
        return new Iterable>() {
            @Override
            public Iterator> iterator() {
                return new HandlerIterator(allBeans());
            }
        };
    }

    @Override
    public Comparator> getPriorityComparator() {
        return new PriorityComparator(getBeanManager().getServices().get(AnnotationApiAbstraction.class));
    }

    private boolean isSessionBeanProxy(T instance) {
        return ejbSupport != null ? ejbSupport.isSessionBeanProxy(instance) : false;
    }

    private void destroyDependentInstance(T instance) {
        CreationalContext ctx = getCreationalContext();
        if (ctx instanceof WeldCreationalContext) {
            WeldCreationalContext weldCtx = cast(ctx);
            weldCtx.destroyDependentInstance(instance);
        }
    }

    private void checkBeanResolved() {
        if (bean != null) {
            return;
        } else if (isUnsatisfied()) {
            throw BeanManagerLogger.LOG.injectionPointHasUnsatisfiedDependencies(Formats.formatAnnotations(ip.getQualifiers()),
                    Formats.formatInjectionPointType(ip.getType()), InjectionPoints.getUnsatisfiedDependenciesAdditionalInfo(ip, getBeanManager()));
        } else {
            throw BeanManagerLogger.LOG.injectionPointHasAmbiguousDependencies(Formats.formatAnnotations(ip.getQualifiers()),
                    Formats.formatInjectionPointType(ip.getType()), WeldCollections.toMultiRowString(allBeans()));
        }
    }

    private T getBeanInstance(Bean bean) {
        final ThreadLocalStackReference stack = currentInjectionPoint.pushConditionally(ip, isRegisterableInjectionPoint());
        try {
            return Reflections. cast(getBeanManager().getReference(bean, getType(), getCreationalContext(), false));
        } finally {
            stack.pop();
        }
    }

    private boolean isRegisterableInjectionPoint() {
        return !getType().equals(InjectionPoint.class);
    }

    private Set> allBeans() {
        return allBeans == null ? resolveBeans() : allBeans;
    }

    private Set> resolveBeans() {
        // Perform typesafe resolution, and possibly attempt to resolve the ambiguity
        Resolvable resolvable = new ResolvableBuilder(getType(), getBeanManager()).addQualifiers(getQualifiers())
            .setDeclaringBean(getInjectionPoint().getBean()).create();
        TypeSafeBeanResolver beanResolver = getBeanManager().getBeanResolver();
        return beanResolver.resolve(beanResolver.resolve(resolvable, Reflections.isCacheable(getQualifiers())));
    }

    // Serialization
    private Object writeReplace() throws ObjectStreamException {
        return new SerializationProxy(this);
    }

    private void readObject(ObjectInputStream stream) throws InvalidObjectException {
        throw BeanLogger.LOG.serializationProxyRequired();
    }

    private static class SerializationProxy extends AbstractFacadeSerializationProxy> {

        private static final long serialVersionUID = 9181171328831559650L;

        public SerializationProxy(InstanceImpl instance) {
            super(instance);
        }

        private Object readResolve() throws ObjectStreamException {
            return InstanceImpl.of(getInjectionPoint(), getCreationalContext(), getBeanManager());
        }

    }

    abstract class BeanIterator implements Iterator {

        protected final Iterator> delegate;

        private BeanIterator(Set> beans) {
            this.delegate = beans.iterator();
        }

        @Override
        public boolean hasNext() {
            return delegate.hasNext();
        }

        @Override
        public void remove() {
            throw BeanLogger.LOG.instanceIteratorRemoveUnsupported();
        }

    }

    class InstanceImplIterator extends BeanIterator {

        private InstanceImplIterator(Set> beans) {
            super(beans);
        }

        @Override
        public T next() {
            return getBeanInstance(delegate.next());
        }
    }

    class HandlerIterator extends BeanIterator> {

        private HandlerIterator(Set> beans) {
            super(beans);
        }

        @Override
        public Handler next() {
            Bean bean = delegate.next();
            return new HandlerImpl<>(() -> getBeanInstance(bean), InstanceImpl.this, bean);
        }

    }

    private static class HandlerImpl implements Handler {

        private final LazyValueHolder value;

        private final Bean bean;

        private final WeakReference> instance;

        private final AtomicBoolean isDestroyed;

        HandlerImpl(Supplier supplier, WeldInstance instance, Bean bean) {
            this.value = LazyValueHolder.forSupplier(supplier);
            this.bean = bean;
            this.instance = new WeakReference<>(instance);
            this.isDestroyed = new AtomicBoolean(false);
        }

        @Override
        public T get() {
            if (!value.isAvailable() && instance.get() == null) {
                // Contextual reference cannot be obtained if the producing Instance does not exist
                throw BeanLogger.LOG.cannotObtainHandlerContextualReference(this);
            }
            return value.get();
        }

        @Override
        public Bean getBean() {
            return bean;
        }

        @Override
        public void destroy() {
            WeldInstance ref = instance.get();
            if (ref == null) {
                BeanLogger.LOG.cannotDestroyHandlerContextualReference(this);
            }
            if (value.isAvailable() && isDestroyed.compareAndSet(false, true)) {
                ref.destroy(value.get());
            }
        }

        @Override
        public void close() {
            destroy();
        }

        @Override
        public String toString() {
            return "HandlerImpl [bean=" + bean + "]";
        }

    }
}