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

org.glassfish.jersey.client.innate.inject.NonInjectionManager Maven / Gradle / Ivy

Go to download

A bundle project producing JAX-RS RI bundles. The primary artifact is an "all-in-one" OSGi-fied JAX-RS RI bundle (jaxrs-ri.jar). Attached to that are two compressed JAX-RS RI archives. The first archive (jaxrs-ri.zip) consists of binary RI bits and contains the API jar (under "api" directory), RI libraries (under "lib" directory) as well as all external RI dependencies (under "ext" directory). The secondary archive (jaxrs-ri-src.zip) contains buildable JAX-RS RI source bundle and contains the API jar (under "api" directory), RI sources (under "src" directory) as well as all external RI dependencies (under "ext" directory). The second archive also contains "build.xml" ANT script that builds the RI sources. To build the JAX-RS RI simply unzip the archive, cd to the created jaxrs-ri directory and invoke "ant" from the command line.

There is a newer version: 3.1.9
Show newest version
/*
 * Copyright (c) 2023, 2024 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package org.glassfish.jersey.client.innate.inject;

import org.glassfish.jersey.client.internal.LocalizationMessages;
import org.glassfish.jersey.internal.inject.Binder;
import org.glassfish.jersey.internal.inject.Binding;
import org.glassfish.jersey.internal.inject.ClassBinding;
import org.glassfish.jersey.internal.inject.DisposableSupplier;
import org.glassfish.jersey.internal.inject.ForeignDescriptor;
import org.glassfish.jersey.internal.inject.InjectionManager;
import org.glassfish.jersey.internal.inject.InstanceBinding;
import org.glassfish.jersey.internal.inject.PerThread;
import org.glassfish.jersey.internal.inject.ServiceHolder;
import org.glassfish.jersey.internal.inject.ServiceHolderImpl;
import org.glassfish.jersey.internal.inject.SupplierClassBinding;
import org.glassfish.jersey.internal.inject.SupplierInstanceBinding;
import org.glassfish.jersey.internal.util.collection.LazyValue;
import org.glassfish.jersey.internal.util.collection.Value;
import org.glassfish.jersey.internal.util.collection.Values;
import org.glassfish.jersey.process.internal.RequestScope;
import org.glassfish.jersey.process.internal.RequestScoped;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import javax.ws.rs.ConstrainedTo;
import javax.ws.rs.RuntimeType;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;

@ConstrainedTo(RuntimeType.CLIENT)
public final class NonInjectionManager implements InjectionManager {
    private static final Logger logger = Logger.getLogger(NonInjectionManager.class.getName());

    private final MultivaluedMap, InstanceBinding> instanceBindings = new MultivaluedHashMap<>();
    private final MultivaluedMap, ClassBinding> contractBindings = new MultivaluedHashMap<>();
    private final MultivaluedMap, SupplierInstanceBinding> supplierInstanceBindings = new MultivaluedHashMap<>();
    private final MultivaluedMap, SupplierClassBinding> supplierClassBindings = new MultivaluedHashMap<>();
    private final MultivaluedMap> instanceTypeBindings = new MultivaluedHashMap<>();
    private final MultivaluedMap> contractTypeBindings = new MultivaluedHashMap<>();
    private final MultivaluedMap> supplierTypeInstanceBindings = new MultivaluedHashMap<>();
    private final MultivaluedMap> supplierTypeClassBindings = new MultivaluedHashMap<>();

    private final MultivaluedMap disposableSupplierObjects = new MultivaluedHashMap<>();

    private final Instances instances = new Instances();
    private final Types types = new Types();

    private volatile boolean isRequestScope = false;
    private volatile boolean shutdown = false;

    /**
     * A class that holds singleton instances and thread-scope instances. Provides thread safe access to singletons
     * and thread-scope instances. The instances are created for Type (ParametrizedType) and for a Class.
     * @param  the type for which the instance is created, either Class, or ParametrizedType (for instance
     * Provider<SomeClass>).
     */
    private class TypedInstances {
        private final MultivaluedMap> singletonInstances = new MultivaluedHashMap<>();
        private ThreadLocal>> threadInstances = new ThreadLocal<>();
        private final List threadPredestroyables = Collections.synchronizedList(new LinkedList<>());

        private  List> _getSingletons(TYPE clazz) {
            List> si;
            synchronized (singletonInstances) {
                si = singletonInstances.get(clazz);
            }
            return si;
        }

        @SuppressWarnings("unchecked")
         T _addSingleton(TYPE clazz, T instance, Binding binding, Annotation[] qualifiers) {
            synchronized (singletonInstances) {
                // check existing singleton with a qualifier already created by another thread io a meantime
                List> values = singletonInstances.get(clazz);
                if (values != null) {
                    List> qualified
                            = values.stream()
                                    .filter(ctx -> ctx.hasQualifiers(qualifiers))
                                    .collect(Collectors.toList());
                    if (!qualified.isEmpty()) {
                        return (T) qualified.get(0).instance;
                    }
                }
                singletonInstances.add(clazz, new InstanceContext<>(instance, binding, qualifiers));
                threadPredestroyables.add(instance);
                return instance;
            }
        }

        @SuppressWarnings("unchecked")
         T addSingleton(TYPE clazz, T t, Binding binding, Annotation[] instanceQualifiers) {
            T t2  = _addSingleton(clazz, t, binding, instanceQualifiers);
            if (t2 == t) {
                for (Type contract : binding.getContracts()) {
                    if (!clazz.equals(contract) && isClass(contract)) {
                        _addSingleton((TYPE) contract, t, binding, instanceQualifiers);
                    }
                }
            }
            return t2;
        }

        private List> _getThreadInstances(TYPE clazz) {
            MultivaluedMap> ti = threadInstances.get();
            List> list = ti == null ? null : new LinkedList<>();
            if (ti != null) {
                return ti.get(clazz);
            }
            return list;
        }

        private  void _addThreadInstance(TYPE clazz, T instance, Binding binding, Annotation[] qualifiers) {
            MultivaluedMap> map = threadInstances.get();
            if (map == null) {
                map = new MultivaluedHashMap<>();
                threadInstances.set(map);
            }
            map.add(clazz, new InstanceContext<>(instance, binding, qualifiers));
            threadPredestroyables.add(instance);
        }

         void addThreadInstance(TYPE clazz, T t, Binding binding, Annotation[] instanceQualifiers) {
            _addThreadInstance(clazz, t, binding, instanceQualifiers);
            for (Type contract : binding.getContracts()) {
                if (!clazz.equals(contract) && isClass(contract)) {
                    _addThreadInstance((TYPE) contract, t, binding, instanceQualifiers);
                }
            }
        }

        private  List getInstances(TYPE clazz, Annotation[] annotations) {
            List> i = _getContexts(clazz);
            return InstanceContext.toInstances(i, annotations);
        }

         List> getContexts(TYPE clazz, Annotation[] annotations) {
            List> i = _getContexts(clazz);
            return InstanceContext.filterInstances(i, annotations);
        }

        private  List> _getContexts(TYPE clazz) {
            List> si = _getSingletons(clazz);
            List> ti = _getThreadInstances(clazz);
            if (si == null && ti != null) {
                si = ti;
            } else if (ti != null) {
                si.addAll(ti);
            }
            return si;
        }

         T getInstance(TYPE clazz, Annotation[] annotations) {
            List i = getInstances(clazz, annotations);
            if (i != null) {
                checkUnique(i);
                return i.get(0);
            }
            return null;
        }

        void dispose() {
            singletonInstances.forEach((clazz, instances) -> instances.forEach(instance -> preDestroy(instance.getInstance())));
            threadPredestroyables.forEach(NonInjectionManager.this::preDestroy);
            /* The java.lang.ThreadLocal$ThreadLocalMap$Entry[] keeps references to this NonInjectionManager */
            threadInstances = null;
        }
    }

    private class Instances extends TypedInstances> {
    }

    private class Types extends TypedInstances {
    }

    public NonInjectionManager() {
    }

    public NonInjectionManager(boolean warning) {
        if (warning) {
            logger.warning(LocalizationMessages.NONINJECT_FALLBACK());
        } else {
            logger.log(Level.FINER, LocalizationMessages.NONINJECT_FALLBACK());
        }
    }

    @Override
    public void completeRegistration() {
        instances._addSingleton(InjectionManager.class, this, new InjectionManagerBinding(), null);
    }

    @Override
    public void shutdown() {
        shutdown = true;

        disposableSupplierObjects.forEach((supplier, objects) -> objects.forEach(supplier::dispose));
        disposableSupplierObjects.clear();

        instances.dispose();
        types.dispose();
    }

    @Override
    public boolean isShutdown() {
        return shutdown;
    }

    private void checkShutdown() {
        if (shutdown) {
            throw new IllegalStateException(LocalizationMessages.NONINJECT_SHUTDOWN());
        }
    }

    @Override
    public void register(Binding binding) {
        checkShutdown();
        if (InstanceBinding.class.isInstance(binding)) {
            InstanceBinding instanceBinding = (InstanceBinding) binding;
            Class mainType = binding.getImplementationType();
            if (!instanceBindings.containsKey(mainType)) { // the class could be registered twice, for reader & for writer
                instanceBindings.add(mainType, (InstanceBinding) binding);
            }
            for (Type type : (Iterable) instanceBinding.getContracts()) {
                if (isClass(type)) {
                    if (!mainType.equals(type)) {
                        instanceBindings.add((Class) type, instanceBinding);
                    }
                } else {
                    instanceTypeBindings.add(type, instanceBinding);
                }
            }
        } else if (ClassBinding.class.isInstance(binding)) {
            ClassBinding contractBinding = (ClassBinding) binding;
            Class mainType = binding.getImplementationType();
            if (!contractBindings.containsKey(mainType)) { // the class could be registered twice, for reader & for writer
                contractBindings.add(mainType, contractBinding);
            }
            for (Type type : contractBinding.getContracts()) {
                if (isClass(type)) {
                    if (!mainType.equals(type)) {
                        contractBindings.add((Class) type, contractBinding);
                    }
                } else {
                    contractTypeBindings.add(type, contractBinding);
                }
            }
        } else if (SupplierInstanceBinding.class.isInstance(binding)) {
            SupplierInstanceBinding supplierBinding = (SupplierInstanceBinding) binding;
            for (Type type : supplierBinding.getContracts()) {
                if (isClass(type)) {
                    supplierInstanceBindings.add((Class) type, supplierBinding);
                } else {
                    supplierTypeInstanceBindings.add(type, supplierBinding);
                }
            }
        } else if (SupplierClassBinding.class.isInstance(binding)) {
            SupplierClassBinding supplierBinding = (SupplierClassBinding) binding;
            for (Type type : supplierBinding.getContracts()) {
                if (isClass(type)) {
                    supplierClassBindings.add((Class) type, supplierBinding);
                } else {
                    supplierTypeClassBindings.add(type, supplierBinding);
                }
            }
        }
    }

    @Override
    public void register(Iterable descriptors) {
        checkShutdown();
        for (Binding binding : descriptors) {
            register(binding);
        }
    }

    @Override
    public void register(Binder binder) {
        checkShutdown();
        binder.getBindings().stream().iterator().forEachRemaining(this::register);
    }

    @Override
    public void register(Object provider) throws IllegalArgumentException {
        throw new UnsupportedOperationException("Register " + provider);
    }

    @Override
    public boolean isRegistrable(Class clazz) {
        return false; // for external creators
    }

    @Override
    public  List> getAllServiceHolders(Class contractOrImpl, Annotation... qualifiers) {
        checkShutdown();

        ClassBindings classBindings = classBindings(contractOrImpl, qualifiers);
        return classBindings.getAllServiceHolders(qualifiers);
    }

    @Override
    public  T getInstance(Class contractOrImpl, Annotation... qualifiers) {
        checkShutdown();

        ClassBindings classBindings = classBindings(contractOrImpl, qualifiers);
        classBindings.matchQualifiers(qualifiers);
        return classBindings.getInstance();
    }

    @Override
    public  T getInstance(Class contractOrImpl, String classAnalyzer) {
        throw new UnsupportedOperationException("getInstance(Class, String)");
    }

    @Override
    public  T getInstance(Class contractOrImpl) {
        checkShutdown();

        T instance = instances.getInstance(contractOrImpl, null);
        if (instance != null) {
            return instance;
        }
        return create(contractOrImpl);
    }

    @Override
    @SuppressWarnings("unchecked")
    public  T getInstance(Type contractOrImpl) {
        checkShutdown();

        if (ParameterizedType.class.isInstance(contractOrImpl)) {
            T instance = types.getInstance(contractOrImpl, null);
            if (instance != null) {
                return instance;
            }

            TypeBindings typeBindings = typeBindings(contractOrImpl);
            return typeBindings.getInstance();
        } else if (isClass(contractOrImpl)) {
            return getInstance((Class) contractOrImpl);
        }
        throw new IllegalStateException(LocalizationMessages.NONINJECT_UNSATISFIED(contractOrImpl));
    }

    private static boolean isClass(Type type) {
        return Class.class.isAssignableFrom(type.getClass());
    }

    @Override
    public Object getInstance(ForeignDescriptor foreignDescriptor) {
        throw new UnsupportedOperationException("getInstance(ForeignDescriptor foreignDescriptor) ");
    }

    @Override
    public ForeignDescriptor createForeignDescriptor(Binding binding) {
        throw new UnsupportedOperationException("createForeignDescriptor(Binding binding) ");
    }

    @Override
    public  List getAllInstances(Type contractOrImpl) {
        checkShutdown();

        if (!isClass(contractOrImpl)) {
            TypeBindings typeBindings = typeBindings(contractOrImpl);
            return typeBindings.allInstances();
        }

        @SuppressWarnings("unchecked")
        ClassBindings classBindings = classBindings((Class) contractOrImpl);
        return classBindings.allInstances();
    }

    @SuppressWarnings("unchecked")
    @Override
    public  T create(Class createMe) {
        checkShutdown();

        if (InjectionManager.class.equals(createMe)) {
            return (T) this;
        }
        if (RequestScope.class.equals(createMe)) {
            if (!isRequestScope) {
                isRequestScope = true;
                return (T) new NonInjectionRequestScope();
            } else {
                throw new IllegalStateException(LocalizationMessages.NONINJECT_REQUESTSCOPE_CREATED());
            }
        }

        ClassBindings classBindings = classBindings(createMe);
        return classBindings.create(true);
    }

    @Override
    public  T createAndInitialize(Class createMe) {
        checkShutdown();

        if (InjectionManager.class.equals(createMe)) {
            return (T) this;
        }
        if (RequestScope.class.equals(createMe)) {
            if (!isRequestScope) {
                isRequestScope = true;
                return (T) new NonInjectionRequestScope();
            } else {
                throw new IllegalStateException(LocalizationMessages.NONINJECT_REQUESTSCOPE_CREATED());
            }
        }

        ClassBindings classBindings = classBindings(createMe);
        T t = classBindings.create(false);
        return t != null ? t : justCreate(createMe);
    }

    public  T justCreate(Class createMe) {
        T result = null;
        try {
            Constructor mostArgConstructor = findConstructor(createMe);
            if (mostArgConstructor != null) {
                int argCount = mostArgConstructor.getParameterCount();
                if (argCount == 0) {
                    ensureAccessible(mostArgConstructor);
                    result = mostArgConstructor.newInstance();
                } else if (argCount > 0) {
                    Object[] args = getArguments(mostArgConstructor, argCount);
                    if (args != null) {
                        ensureAccessible(mostArgConstructor);
                        result = mostArgConstructor.newInstance(args);
                    }
                }
            }
            if (result == null) {
                throw new IllegalStateException(LocalizationMessages.NONINJECT_NO_CONSTRUCTOR(createMe.getName()));
            }
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }

        inject(result);
        return result;
    }

    private static  Constructor findConstructor(Class forClass) {
        Constructor[] constructors = (Constructor[]) forClass.getDeclaredConstructors();
        Constructor mostArgConstructor = null;
        int argCount = -1;
        for (Constructor constructor : constructors) {
            if (constructor.isAnnotationPresent(Inject.class) || constructor.getParameterCount() == 0) {
                if (constructor.getParameterCount() > argCount) {
                    mostArgConstructor = constructor;
                    argCount = constructor.getParameterCount();
                }
            }
        }
        return mostArgConstructor;
    }

    private Object[] getArguments(Executable executable, int argCount) {
        if (executable == null) {
            return null;
        }
        Object[] args = new Object[argCount];
        for (int i = 0; i != argCount; i++) {
            Type type = executable.getAnnotatedParameterTypes()[i].getType();
            args[i] = isClass(type) ? getInstance((Class) type) : getInstance(type);
        }
        return args;
    }

    private static void ensureAccessible(Executable executable) {
        try {
            if (!executable.isAccessible()) {
                executable.setAccessible(true);
            }
        } catch (Exception e) {
            // consume. It will fail later with invoking the executable
        }
    }

    private void checkUnique(List list) {
        if (list.size() != 1) {
            throw new IllegalStateException(LocalizationMessages.NONINJECT_AMBIGUOUS_SERVICES(list.get(0)));
        }
    }

    @Override
    public void inject(Object injectMe) {
        Method postConstruct = getAnnotatedMethod(injectMe, PostConstruct.class);
        if (postConstruct != null) {
            ensureAccessible(postConstruct);
            try {
                postConstruct.invoke(injectMe);
            } catch (Exception e) {
                throw new IllegalStateException(e);
            }
        }
    }

    @Override
    public void inject(Object injectMe, String classAnalyzer) {
        throw new UnsupportedOperationException("inject(Object injectMe, String classAnalyzer)");
    }

    @Override
    public void preDestroy(Object preDestroyMe) {
        Method preDestroy = getAnnotatedMethod(preDestroyMe, PreDestroy.class);
        if (preDestroy != null) {
            ensureAccessible(preDestroy);
            try {
                preDestroy.invoke(preDestroyMe);
            } catch (Exception e) {
                throw new IllegalStateException(e);
            }
        }
    }

    private static Method getAnnotatedMethod(Object object, Class annotation) {
        Class clazz = object.getClass();
        for (Method method : clazz.getMethods()) {
            if (method.isAnnotationPresent(annotation)
                    && /* do not invoke interceptors */ method.getParameterCount() == 0) {
                return method;
            }
        }
        return null;
    }

    /**
     * Some {@link Binding} requires the proxy to be created rather than just the instance,
     * for instance a proxy of an instance supplied by a supplier that is not known at a time of the proxy creation.
     * @param createProxy the nullable {@link Binding#isProxiable()} information
     * @param iface the type of which the proxy is created
     * @param supplier the reference to the supplier
     * @param  the type the supplier should supply
     * @return The proxy for the instance supplied by a supplier or the instance if not required to be proxied.
     */
    @SuppressWarnings("unchecked")
    private  T createSupplierProxyIfNeeded(Boolean createProxy, Class iface, Supplier supplier) {
        if (createProxy != null && createProxy && iface.isInterface()) {
            T proxy = (T) Proxy.newProxyInstance(iface.getClassLoader(), new Class[]{iface}, new InvocationHandler() {
                final SingleRegisterSupplier singleSupplierRegister = new SingleRegisterSupplier<>(supplier);
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    T t = singleSupplierRegister.get();
                    Object ret = method.invoke(t, args);
                    return ret;
                }
            });
            return proxy;
        } else {
            return registerDisposableSupplierAndGet(supplier);
        }
    }

    /**
     * A holder class making sure the Supplier (especially the {@link DisposableSupplier}) supplying the instance
     * supplies (and is registered for being disposed at the end of the lifecycle) only once.
     * @param 
     */
    private class SingleRegisterSupplier {
        private final LazyValue once;

        private SingleRegisterSupplier(Supplier supplier) {
            once = Values.lazy((Value) () -> registerDisposableSupplierAndGet(supplier));
        }

        T get() {
            return once.get();
        }
    }

    private  T registerDisposableSupplierAndGet(Supplier supplier) {
        T instance = supplier.get();
        if (DisposableSupplier.class.isInstance(supplier)) {
            disposableSupplierObjects.add((DisposableSupplier) supplier, instance);
        }
        return instance;
    }

    /**
     * Create {@link ClassBindings} instance containing bindings and instances for the given Type.
     * @param clazz the given class.
     * @param instancesQualifiers The qualifiers the expected instances of the given class should have.
     * @param  Expected return class type.
     * @return the {@link ClassBindings}.
     */
    @SuppressWarnings("unchecked")
    private  ClassBindings classBindings(Class clazz, Annotation... instancesQualifiers) {
        ClassBindings classBindings = new ClassBindings<>(clazz, instancesQualifiers);
        List> ib = instanceBindings.get(clazz);
        if (ib != null) {
            ib.forEach(binding -> classBindings.instanceBindings.add((InstanceBinding) binding));
        }
        List> sib = supplierInstanceBindings.get(clazz);
        if (sib != null) {
            sib.forEach(binding -> classBindings.supplierInstanceBindings.add((SupplierInstanceBinding) binding));
        }
        List> cb = contractBindings.get(clazz);
        if (cb != null) {
            cb.forEach(binding -> classBindings.classBindings.add((ClassBinding) binding));
        }
        List> scb = supplierClassBindings.get(clazz);
        if (scb != null) {
            scb.forEach(binding -> classBindings.supplierClassBindings.add((SupplierClassBinding) binding));
        }
        return classBindings;
    }

    /**
     * Create {@link TypeBindings} instance containing bindings and instances for the given Type.
     * @param type the given type.
     * @param  Expected return type.
     * @return the {@link TypeBindings}.
     */
    @SuppressWarnings("unchecked")
    private  TypeBindings typeBindings(Type type) {
        TypeBindings typeBindings = new TypeBindings<>(type);
        List> ib = instanceTypeBindings.get(type);
        if (ib != null) {
            ib.forEach(binding -> typeBindings.instanceBindings.add((InstanceBinding) binding));
        }
        List> sib = supplierTypeInstanceBindings.get(type);
        if (sib != null) {
            sib.forEach(binding -> typeBindings.supplierInstanceBindings.add((SupplierInstanceBinding) binding));
        }
        List> cb = contractTypeBindings.get(type);
        if (cb != null) {
            cb.forEach(binding -> typeBindings.classBindings.add((ClassBinding) binding));
        }
        List> scb = supplierTypeClassBindings.get(type);
        if (scb != null) {
            scb.forEach(binding -> typeBindings.supplierClassBindings.add((SupplierClassBinding) binding));
        }
        return typeBindings;
    }

    /**
     * 

* A class that contains relevant bindings for a given TYPE, filtered from all registered bindings. * The TYPE is either Type (ParametrizedType) or Class. *

*

* The class also filters any bindings for which the singleton or thread-scoped instance already is created. * The class either provides the existing instance, or all instances of the TYPE, or {@link ServiceHolder}s. *

* @param The expected return type for the TYPE. * @param The Type for which a {@link Binding} has been created. */ private abstract class XBindings { protected final List> instanceBindings = new LinkedList<>(); protected final List> supplierInstanceBindings = new LinkedList<>(); protected final List> classBindings = new LinkedList<>(); protected final List> supplierClassBindings = new LinkedList<>(); protected final TYPE type; protected final Annotation[] instancesQualifiers; protected final TypedInstances instances; protected XBindings(TYPE type, Annotation[] instancesQualifiers, TypedInstances instances) { this.type = type; this.instancesQualifiers = instancesQualifiers; this.instances = instances; } int size() { return instanceBindings.size() + supplierInstanceBindings.size() + classBindings.size() + supplierClassBindings.size(); } private void _checkUnique() { if (size() > 1) { throw new IllegalStateException(LocalizationMessages.NONINJECT_AMBIGUOUS_SERVICES(type)); } } void filterBinding(Binding binding) { if (InstanceBinding.class.isInstance(binding)) { instanceBindings.remove(binding); } else if (ClassBinding.class.isInstance(binding)) { classBindings.remove(binding); } else if (SupplierInstanceBinding.class.isInstance(binding)) { supplierInstanceBindings.remove(binding); } else if (SupplierClassBinding.class.isInstance(binding)) { supplierClassBindings.remove(binding); } } /** * Match the binging qualifiers * @param bindingQualifiers the qualifiers registered with the bindings */ void matchQualifiers(Annotation... bindingQualifiers) { if (bindingQualifiers != null) { _filterRequested(instanceBindings, bindingQualifiers); _filterRequested(classBindings, bindingQualifiers); _filterRequested(supplierInstanceBindings, bindingQualifiers); _filterRequested(supplierClassBindings, bindingQualifiers); } } @SuppressWarnings("unchecked") private void _filterRequested(List> bindingList, Annotation... requestedQualifiers) { for (Iterator bindingIterator = bindingList.iterator(); bindingIterator.hasNext();) { Binding binding = bindingIterator.next(); classLoop: for (Annotation requestedQualifier : requestedQualifiers) { for (Annotation bindingQualifier : binding.getQualifiers()) { if (requestedQualifier.annotationType().isInstance(bindingQualifier)) { continue classLoop; } } bindingIterator.remove(); } } } protected boolean _isPerThread(Class scope) { return RequestScoped.class.equals(scope) || PerThread.class.equals(scope); } private X _getInstance(InstanceBinding instanceBinding) { return instanceBinding.getService(); } private X _create(SupplierInstanceBinding binding) { Supplier supplier = binding.getSupplier(); X t = registerDisposableSupplierAndGet(supplier); if (Singleton.class.equals(binding.getScope())) { _addInstance(t, binding); } else if (_isPerThread(binding.getScope())) { _addThreadInstance(t, binding); } return t; } X create(boolean throwWhenNoBinding) { _checkUnique(); if (!instanceBindings.isEmpty()) { return _getInstance(instanceBindings.get(0)); } else if (!supplierInstanceBindings.isEmpty()) { return _create(supplierInstanceBindings.get(0)); } else if (!classBindings.isEmpty()) { return _createAndStore(classBindings.get(0)); } else if (!supplierClassBindings.isEmpty()) { return _create(supplierClassBindings.get(0)); } if (throwWhenNoBinding) { throw new IllegalStateException(LocalizationMessages.NONINJECT_NO_BINDING(type)); } else { return null; } } protected X getInstance() { X instance = instances.getInstance(type, instancesQualifiers); if (instance != null) { return instance; } return create(true); } List allInstances() { List list = new LinkedList<>(); List> instanceContextList; instanceContextList = instances.getContexts(type, instancesQualifiers); if (instanceContextList != null) { instanceContextList.forEach(instanceContext -> filterBinding(instanceContext.getBinding())); instanceContextList.forEach(instanceContext -> list.add((X) instanceContext.getInstance())); } list.addAll(instanceBindings.stream() .map(this::_getInstance) .collect(Collectors.toList())); list.addAll(classBindings.stream() .map(this::_createAndStore) .collect(Collectors.toList())); list.addAll(supplierInstanceBindings.stream() .map(this::_create) .collect(Collectors.toList())); list.addAll(supplierClassBindings.stream() .map(this::_create) .collect(Collectors.toList())); return list; } protected abstract X _create(SupplierClassBinding binding); protected abstract X _createAndStore(ClassBinding binding); protected T _addInstance(TYPE type, T instance, Binding binding) { return instances.addSingleton(type, instance, binding, instancesQualifiers); } protected void _addThreadInstance(TYPE type, Object instance, Binding binding) { instances.addThreadInstance(type, instance, binding, instancesQualifiers); } protected T _addInstance(T instance, Binding binding) { return instances.addSingleton(type, instance, binding, instancesQualifiers); } protected void _addThreadInstance(Object instance, Binding binding) { instances.addThreadInstance(type, instance, binding, instancesQualifiers); } } private class ClassBindings extends XBindings> { private ClassBindings(Class clazz, Annotation[] instancesQualifiers) { super(clazz, instancesQualifiers, NonInjectionManager.this.instances); } @SuppressWarnings("unchecked") List> getAllServiceHolders(Annotation... qualifiers) { matchQualifiers(qualifiers); List> holders = new LinkedList<>(); List> instanceContextList; instanceContextList = instances.getContexts(type, qualifiers); if (instanceContextList != null) { instanceContextList.forEach(instanceContext -> filterBinding(instanceContext.getBinding())); instanceContextList.forEach(instanceContext -> holders.add(new ServiceHolderImpl( (T) instanceContext.getInstance(), (Class) instanceContext.getInstance().getClass(), instanceContext.getBinding().getContracts(), instanceContext.getBinding().getRank() == null ? 0 : instanceContext.getBinding().getRank()) )); } List> instanceBindingHolders = instanceBindings.stream() .map(this::_serviceHolder) .collect(Collectors.toList()); holders.addAll(instanceBindingHolders); List> classBindingHolders = classBindings.stream() .filter(binding -> NonInjectionManager.this.findConstructor(binding.getService()) != null) .map(this::_serviceHolder) .collect(Collectors.toList()); holders.addAll(classBindingHolders); return holders; } private ServiceHolderImpl _serviceHolder(InstanceBinding binding) { return new ServiceHolderImpl( binding.getService(), binding.getImplementationType(), binding.getContracts(), binding.getRank() == null ? 0 : binding.getRank()); } private ServiceHolderImpl _serviceHolder(ClassBinding binding) { return new ServiceHolderImpl( NonInjectionManager.this.create(binding.getService()), binding.getImplementationType(), binding.getContracts(), binding.getRank() == null ? 0 : binding.getRank()); } protected T _create(SupplierClassBinding binding) { Supplier supplier = instances.getInstance(binding.getSupplierClass(), null); if (supplier == null) { supplier = justCreate(binding.getSupplierClass()); if (Singleton.class.equals(binding.getSupplierScope())) { supplier = instances.addSingleton(binding.getSupplierClass(), supplier, binding, null); } else if (_isPerThread(binding.getSupplierScope())) { instances.addThreadInstance(binding.getSupplierClass(), supplier, binding, null); } } T t = createSupplierProxyIfNeeded(binding.isProxiable(), (Class) type, supplier); if (Singleton.class.equals(binding.getScope())) { t = _addInstance(type, t, binding); } else if (_isPerThread(binding.getScope())) { _addThreadInstance(type, t, binding); } return t; } protected T _createAndStore(ClassBinding binding) { T result = justCreate(binding.getService()); result = _addInstance(binding.getService(), result, binding); return result; } } private class TypeBindings extends XBindings { private TypeBindings(Type type) { super(type, null, types); } protected T _create(SupplierClassBinding binding) { Supplier supplier = justCreate(binding.getSupplierClass()); T t = registerDisposableSupplierAndGet(supplier); if (Singleton.class.equals(binding.getScope())) { t = _addInstance(type, t, binding); } else if (_isPerThread(binding.getScope())) { _addThreadInstance(type, t, binding); } return t; } @Override protected T _createAndStore(ClassBinding binding) { T result = justCreate(binding.getService()); result = _addInstance(type, result, binding); return result; } @SuppressWarnings("unchecked") @Override T create(boolean throwWhenNoBinding) { if (ParameterizedType.class.isInstance(type)) { ParameterizedType pt = (ParameterizedType) type; if (Provider.class.equals(pt.getRawType())) { return (T) new Provider() { final SingleRegisterSupplier supplier = new SingleRegisterSupplier<>(new Supplier() { @Override public Object get() { Type actualTypeArgument = pt.getActualTypeArguments()[0]; if (isClass(actualTypeArgument)) { return NonInjectionManager.this.getInstance((Class) actualTypeArgument); } else { return NonInjectionManager.this.getInstance(actualTypeArgument); } } }); @Override public Object get() { return supplier.get(); //Not disposable } }; } } return super.create(throwWhenNoBinding); } } /** * A triplet of created instance, the registered {@link Binding} that prescribed the creation of the instance * and {@link Annotation qualifiers} the instance was created with. * @param type of the instance. * @see NonInjectionManager#getInstance(Class, Annotation[]) */ private static class InstanceContext { private final T instance; private final Binding binding; private final Annotation[] createdWithQualifiers; private InstanceContext(T instance, Binding binding, Annotation[] qualifiers) { this.instance = instance; this.binding = binding; this.createdWithQualifiers = qualifiers; } public Binding getBinding() { return binding; } public T getInstance() { return instance; } @SuppressWarnings("unchecked") static List toInstances(List> instances, Annotation[] qualifiers) { return instances != null ? instances.stream() .filter(instance -> instance.hasQualifiers(qualifiers)) .map(pair -> (T) pair.getInstance()) .collect(Collectors.toList()) : null; } private static List> filterInstances(List> instances, Annotation... qualifiers) { return instances != null ? instances.stream() .filter(instance -> instance.hasQualifiers(qualifiers)) .collect(Collectors.toList()) : null; } private boolean hasQualifiers(Annotation[] requested) { if (requested != null) { classLoop: for (Annotation req : requested) { if (createdWithQualifiers != null) { for (Annotation cur : createdWithQualifiers) { if (cur.annotationType().isInstance(req)) { continue classLoop; } } return false; } } } return true; } } /** * Singleton Binding this {@link NonInjectionManager} was supposed to be created based upon. */ private static final class InjectionManagerBinding extends Binding> { } }