InitializableSupplierInstanceBinding.java

/*
 * Copyright (c) 2021 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.inject.weld.internal.inject;

import org.glassfish.jersey.internal.inject.AliasBinding;
import org.glassfish.jersey.internal.inject.Binding;
import org.glassfish.jersey.internal.inject.DisposableSupplier;
import org.glassfish.jersey.internal.inject.SupplierInstanceBinding;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;

/**
 * Supplier instance binding to be created in the pre-initialization phase and initialized in runtime.
 * @param <T> Type of the supplied service described by this injection binding.
 */
public class InitializableSupplierInstanceBinding<T>
        extends MatchableBinding<Supplier<T>, InitializableSupplierInstanceBinding<T>>
        implements Cloneable {

    private final InitializableSupplier<T> supplier;

    /**
     * Creates a supplier as an instance.
     *
     * @param supplier service's instance.
     */
    public InitializableSupplierInstanceBinding(Supplier<T> supplier) {
        this.supplier = DisposableSupplier.class.isInstance(supplier)
                ? new InitializableDisposableSupplier<T>((DisposableSupplier<T>) supplier)
                : new InitializableSupplier<T>(supplier);
        if ("EmptyReferenceFactory".equals(supplier.getClass().getSimpleName())) {
            this.supplier.init(supplier);
            this.supplier.isReferencingFactory = true;
        }
        if ("InitializedReferenceFactory".equals(supplier.getClass().getSimpleName())) {
            this.supplier.init(supplier);
            this.supplier.isReferencingFactory = true;
        }
        T t = supplier.get();
    }

    public void init(Supplier<T> supplier) {
        this.supplier.init(supplier);
    }

    public boolean isInit() {
        return supplier.init.get();
    }

    /**
     * Gets supplier's instance.
     *
     * @return supplier's instance.
     */
    public Supplier<T> getSupplier() {
        return supplier;
    }

    public Supplier<T> getOriginalSupplier() {
        if (supplier.originalSupplier == null) {
            throw new IllegalStateException("Supplier must not be null");
        }
        return supplier.originalSupplier;
    }

    public static <T> InitializableSupplierInstanceBinding<T> from(SupplierInstanceBinding<T> binding) {
        return new InitializableSupplierWrappingInstanceBinding(binding);
    }

    @Override
    public InitializableSupplierInstanceBinding clone() {
        throw new RuntimeException(new CloneNotSupportedException());
    }

    public Matching matches(SupplierInstanceBinding<T> other) {
        return matches(this.getOriginalSupplier().getClass(), other.getSupplier().getClass(), other);
    }

    public Matching matches(InitializableSupplierInstanceBinding<T> other) {
        return matches(this.getOriginalSupplier().getClass(), other.getSupplier().getClass(), other);
    }

    private Matching<InitializableSupplierInstanceBinding<T>> matches(
            Class<?> originalSupplierClass, Class<?> otherSupplierClass, Binding other) {
        final boolean matchesService = originalSupplierClass.equals(otherSupplierClass);
        final Matching matching = matchesContracts(other);
        if (matching.matchLevel == MatchLevel.FULL_CONTRACT && matchesService) {
            matching.matchLevel = MatchLevel.SUPPLIER;
        }
        return matching;
    }

    @Override
    protected MatchLevel bestMatchLevel() {
        return MatchLevel.SUPPLIER;
    }

    private static class InitializableDisposableSupplier<T> extends InitializableSupplier<T> implements DisposableSupplier<T> {
        private InitializableDisposableSupplier(DisposableSupplier<T> originalSupplier) {
            super(originalSupplier);
        }

        @Override
        public void dispose(T instance) {
            ((DisposableSupplier) supplier).dispose(instance);
        }
    }

    private static class InitializableSupplier<T> implements Supplier<T> {

        private AtomicBoolean init = new AtomicBoolean(false);
        protected Supplier<T> supplier;
        protected final Supplier<T> originalSupplier;
        private boolean isReferencingFactory = false;

        private InitializableSupplier(Supplier<T> originalSupplier) {
            this.originalSupplier = originalSupplier;
        }

        private void init(Supplier<T> supply) {
            if (!init.getAndSet(true)) {
                this.supplier = supply;
            } else if (!isReferencingFactory && supplier != supply) {
                throw new IllegalStateException("Multiple initialized for " + originalSupplier.getClass());
            }
        }

        @Override
        public T get() {
            if (!init.get()) {
                throw new IllegalStateException("Not initialized" + originalSupplier.getClass());
            }
            return supplier.get();
        }

        public boolean isInit() {
            return init.get();
        }
    }

    private static class InitializableSupplierWrappingInstanceBinding<T> extends InitializableSupplierInstanceBinding<T> {
        private final SupplierInstanceBinding wrapped;
        public InitializableSupplierWrappingInstanceBinding(SupplierInstanceBinding binding) {
            super(binding.getSupplier());
            wrapped = binding;
        }

        private InitializableSupplierWrappingInstanceBinding(InitializableSupplierWrappingInstanceBinding binding) {
            super(binding.getOriginalSupplier());
            wrapped = binding.wrapped;
        }

        @Override
        public Class getImplementationType() {
            return super.getImplementationType();
        }

        @Override
        public Supplier getSupplier() {
            return super.getSupplier();
        }

        @Override
        public Class<? extends Annotation> getScope() {
            return wrapped.getScope();
        }

        @Override
        public Set<Type> getContracts() {
            return wrapped.getContracts();
        }

        @Override
        public Integer getRank() {
            return wrapped.getRank();
        }

        @Override
        public Set<AliasBinding> getAliases() {
            return wrapped.getAliases();
        }

        @Override
        public Set<Annotation> getQualifiers() {
            return wrapped.getQualifiers();
        }

        @Override
        public String getAnalyzer() {
            return wrapped.getAnalyzer();
        }

        @Override
        public String getName() {
            return wrapped.getName();
        }

        @Override
        public InitializableSupplierWrappingInstanceBinding clone() {
            return new InitializableSupplierWrappingInstanceBinding(this);
        }

        @Override
        public String toString() {
            return "InitializableSupplierWrappingInstanceBinding(" +  wrapped.getSupplier() + ")";
        }
    }

}