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

io.smallrye.beanbag.Bean Maven / Gradle / Ivy

The newest version!
package io.smallrye.beanbag;

import java.util.Comparator;
import java.util.Set;

import io.smallrye.common.constraint.Assert;

/**
 * A holder for an instance of a bean with a given definition within a given scope.
 *
 * @param  the bean type (which is usually, but not always, the concrete type of the instance)
 */
final class Bean implements BeanSupplier {
    private static final Comparator> BY_PRIORITY = Comparator.> comparingInt(Bean::getPriority).reversed();

    @SuppressWarnings({ "unchecked", "rawtypes" })
    static  Comparator> byPriority() {
        return (Comparator>) (Comparator) BY_PRIORITY;
    }

    private final BeanDefinition definition;

    private volatile Result result;

    Bean(final BeanDefinition definition) {
        this.definition = definition;
        this.result = new Pending(definition.getBeanSupplier());
    }

    BeanDefinition getDefinition() {
        return definition;
    }

    Class getType() {
        return definition.getType();
    }

    int getPriority() {
        return definition.getPriority();
    }

    String getName() {
        return definition.getName();
    }

    public T get(Scope scope) throws BeanInstantiationException {
        return result.get(scope);
    }

    boolean matchesByType(final Class type) {
        final Set> restrictedTypes = definition.getRestrictedTypes();
        if (!type.isAssignableFrom(definition.getType())) {
            // cannot be assigned
            return false;
        }
        if (restrictedTypes.isEmpty()) {
            return true;
        } else {
            for (Class restrictedType : restrictedTypes) {
                if (restrictedType.isAssignableFrom(type)) {
                    return true;
                }
            }
            return false;
        }
    }

    static abstract class Result {
        abstract T get(Scope scope) throws BeanInstantiationException;
    }

    static final class Failed extends Result {
        private final String message;
        private final Throwable cause;

        Failed(final String message, final Throwable cause) {
            this.message = message;
            this.cause = cause;
        }

        T get(final Scope scope) throws BeanInstantiationException {
            throw cause == null ? new BeanInstantiationException(message) : new BeanInstantiationException(message, cause);
        }
    }

    static final class Instantiated extends Result {
        private final T instance;

        Instantiated(final T instance) {
            this.instance = instance;
        }

        T get(final Scope scope) {
            return instance;
        }
    }

    static final Result MISSING = new Result<>() {
        Object get(final Scope scope) {
            return null;
        }
    };

    @SuppressWarnings("unchecked")
    static  Result missing() {
        return (Result) MISSING;
    }

    /* non-static */ final class Pending extends Result {
        private final BeanSupplier provider;

        Pending(final BeanSupplier provider) {
            Assert.checkNotNullParam("provider", provider);
            this.provider = provider;
        }

        T get(final Scope scope) throws BeanInstantiationException {
            T object;
            synchronized (Bean.this) {
                final Result existing = Bean.this.result;
                if (existing != this) {
                    return existing.get(scope);
                }
                try {
                    object = provider.get(scope);
                } catch (BeanInstantiationException bie) {
                    final Failed failed = new Failed<>(bie.getMessage(), bie);
                    Bean.this.result = failed;
                    return failed.get(scope);
                } catch (Throwable t) {
                    final Failed failed = new Failed<>("Failed to instantiate a bean", t);
                    Bean.this.result = failed;
                    return failed.get(scope);
                }
                Bean.this.result = object == null ? missing() : new Instantiated<>(object);
            }
            return object;
        }
    }

    public String toString() {
        return "Instance for " + definition.getType() + ", name=" + getName() + ", types=" + definition.getRestrictedTypes();
    }
}