org.springframework.data.gemfire.support.GemfireBeanFactoryLocator.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.data.gemfire.support.GemfireBeanFactoryLocator.java

Source

/*
 * Copyright 2016-2019 the original author or authors.
 *
 * 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
 *
 *      https://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.springframework.data.gemfire.support;

import static org.springframework.data.gemfire.support.GemfireBeanFactoryLocator.BeanFactoryReference.newBeanFactoryReference;
import static org.springframework.data.gemfire.util.CollectionUtils.nullSafeSet;
import static org.springframework.data.gemfire.util.SpringUtils.nullOrEquals;

import java.util.Collections;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicReference;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

/**
 * The {@link GemfireBeanFactoryLocator} class stores a reference to the Spring
 * {@link org.springframework.context.ApplicationContext} / {@link BeanFactory} needed to auto-wire
 * user application Pivotal GemFire objects implementing the {@link org.apache.geode.cache.Declarable} interface
 * and defined in Pivotal GemFire's native configuration format (e.g. {@literal cache.xml}.
 *
 * In most cases, a developer does not need to use this class directly as it is registered by the
 * {@link org.springframework.data.gemfire.CacheFactoryBean} when the {@literal useBeanFactoryLocator} property
 * is set, and used internally by bothe the {@link WiringDeclarableSupport} and {@link LazyWiringDeclarableSupport}
 * SDG classes.
 *
 * @author Costin Leau
 * @author John Blum
 * @see LazyWiringDeclarableSupport
 * @see WiringDeclarableSupport
 */
@SuppressWarnings("all")
public class GemfireBeanFactoryLocator
        implements BeanFactoryAware, BeanNameAware, DisposableBean, InitializingBean {

    // Bean alias/name <-> BeanFactory mapping
    protected static final ConcurrentMap<String, BeanFactory> BEAN_FACTORIES = new ConcurrentHashMap<>();

    private BeanFactory beanFactory;

    protected final Logger logger = LoggerFactory.getLogger(getClass());

    private Set<String> associatedBeanNameWithAliases = Collections.emptySet();

    private String associatedBeanName;

    /**
     * Cleans up all {@link BeanFactory} references tracked by this locator.
     */
    public static void clear() {
        BEAN_FACTORIES.clear();
    }

    /**
     * Factory method to construct a new, initialized instance of {@link GemfireBeanFactoryLocator}.
     *
     * @return a new, initialized instance of the {@link GemfireBeanFactoryLocator}.
     * @see GemfireBeanFactoryLocator
     * @see #GemfireBeanFactoryLocator()
     * @see #afterPropertiesSet()
     */
    public static GemfireBeanFactoryLocator newBeanFactoryLocator() {

        GemfireBeanFactoryLocator beanFactoryLocator = new GemfireBeanFactoryLocator();

        beanFactoryLocator.afterPropertiesSet();

        return beanFactoryLocator;
    }

    /**
     * Factory method to construct a new, initialized instance of {@link GemfireBeanFactoryLocator} with the given
     * default Spring {@link BeanFactory} and associated Spring bean name.
     *
     * @param beanFactory reference to the {@link BeanFactory} used to resolve Spring bean references.
     * @param associatedBeanName {@link String} contain the name of the Spring bean associated with
     * the Spring {@link BeanFactory}.
     * @return a new, initialized instance of {@link GemfireBeanFactoryLocator} with the given default
     * Spring {@link BeanFactory} and associated Spring bean name.
     * @see org.springframework.beans.factory.BeanFactory
     * @see GemfireBeanFactoryLocator
     * @see #GemfireBeanFactoryLocator()
     * @see #setBeanFactory(BeanFactory)
     * @see #setBeanName(String)
     * @see #afterPropertiesSet()
     */
    public static GemfireBeanFactoryLocator newBeanFactoryLocator(BeanFactory beanFactory,
            String associatedBeanName) {

        Assert.isTrue(beanFactory == null || StringUtils.hasText(associatedBeanName),
                "associatedBeanName must be specified when BeanFactory is not null");

        GemfireBeanFactoryLocator beanFactoryLocator = new GemfireBeanFactoryLocator();

        beanFactoryLocator.setBeanFactory(beanFactory);
        beanFactoryLocator.setBeanName(associatedBeanName);
        beanFactoryLocator.afterPropertiesSet();

        return beanFactoryLocator;
    }

    /**
     * Resolves the {@link BeanFactory} mapped to the given {@code beanFactoryKey}.
     *
     * @param beanFactoryKey {@link String} value containing the key used to lookup the {@link BeanFactory}.
     * @return the {@link BeanFactory} mapped to the given key.
     * @throws IllegalArgumentException if a Spring {@link BeanFactory} could not be found
     * for the given {@code beanFactoryKey}.
     * @see org.springframework.beans.factory.BeanFactory
     */
    protected static BeanFactory resolveBeanFactory(String beanFactoryKey) {

        BeanFactory beanFactory = BEAN_FACTORIES.get(beanFactoryKey);

        Assert.isTrue(BEAN_FACTORIES.isEmpty() || beanFactory != null,
                String.format("BeanFactory for key [%s] was not found", beanFactoryKey));

        return beanFactory;
    }

    /**
     * Resolves a single Spring {@link BeanFactory} from the mapping of registered bean factories.
     *
     * This class method is synchronized because it contains a "compound action", even though separate actions
     * are performed on a {@link ConcurrentMap}, the actions are not independent and therefore must operate
     * atomically.
     *
     * @return a single Spring {@link BeanFactory} from the registry.
     * @throws IllegalStateException if the registry contains more than 1, or no
     * Spring {@link BeanFactory bean factories}.
     * @see org.springframework.beans.factory.BeanFactory
     */
    protected static synchronized BeanFactory resolveSingleBeanFactory() {

        if (!BEAN_FACTORIES.isEmpty()) {

            boolean allTheSameBeanFactory = true;

            BeanFactory currentBeanFactory = null;

            for (BeanFactory beanFactory : BEAN_FACTORIES.values()) {

                allTheSameBeanFactory &= nullOrEquals(currentBeanFactory, beanFactory);
                currentBeanFactory = beanFactory;

                if (!allTheSameBeanFactory) {
                    break;
                }
            }

            Assert.state(allTheSameBeanFactory,
                    String.format(
                            "BeanFactory key must be specified when more than one BeanFactory %s is registered",
                            new TreeSet(BEAN_FACTORIES.keySet()).toString()));

            return BEAN_FACTORIES.values().iterator().next();
        }

        return null;
    }

    /**
     * Registers all the provided names for given Spring {@link BeanFactory}.
     *
     * @param names {@link Set} of names and aliases to associate with the Spring {@link BeanFactory}.
     * @param beanFactory reference to the Spring {@link BeanFactory}.
     * @see org.springframework.beans.factory.BeanFactory
     * @throws IllegalArgumentException if {@link BeanFactory} is {@literal null}.
     * @throws IllegalStateException if one of the provided names is already associated with
     * an existing, other than given, Spring {@link BeanFactory}.
     * @see org.springframework.beans.factory.BeanFactory
     */
    protected static synchronized void registerAliases(Set<String> names, BeanFactory beanFactory) {

        Set<String> safeNames = nullSafeSet(names);

        Assert.isTrue(safeNames.isEmpty() || beanFactory != null,
                "BeanFactory must not be null when aliases are specified");

        for (String name : safeNames) {

            BeanFactory existingBeanFactory = BEAN_FACTORIES.putIfAbsent(name, beanFactory);

            Assert.isTrue(nullOrEquals(existingBeanFactory, beanFactory),
                    String.format("BeanFactory reference already exists for key [%s]", name));
        }
    }

    /**
     * Removes all Spring {@link BeanFactory} associations/mappings for the given {@link Set} of names.
     *
     * @param names {@link Set} of names identifying the associations/mappings to remove.
     * @return a boolean value indicating whether all associations/mappings were removed successfully.
     */
    protected static synchronized boolean unregisterAliases(Set<String> names) {
        return BEAN_FACTORIES.keySet().removeAll(names);
    }

    /**
     * @inheritDoc
     */
    @Override
    public void afterPropertiesSet() {

        BeanFactory beanFactory = getBeanFactory();

        registerAliases(resolveAndInitializeBeanNamesWithAliases(beanFactory), beanFactory);
    }

    /**
     * Resolves all names (including aliases) from the given Spring {@link BeanFactory}
     * assigned to the {@link #getAssociatedBeanName()}.
     *
     * @param beanFactory {@link BeanFactory} used to resolve the names assigned to the Spring bean
     * identified by the {@link #getAssociatedBeanName()}.
     * @return a {@link Set} of all the names assigned to the Spring bean identified by
     * the {@link #getAssociatedBeanName()} using the provided Spring {@link BeanFactory}.
     * @see org.springframework.beans.factory.BeanFactory
     * @see #getAssociatedBeanName()
     */
    Set<String> resolveAndInitializeBeanNamesWithAliases(BeanFactory beanFactory) {

        String associatedBeanName = getAssociatedBeanName();

        if (beanFactory != null && StringUtils.hasText(associatedBeanName)) {

            String[] beanAliases = beanFactory.getAliases(associatedBeanName);

            this.associatedBeanNameWithAliases = new TreeSet<>();
            this.associatedBeanNameWithAliases.add(associatedBeanName);

            Collections.addAll(this.associatedBeanNameWithAliases, beanAliases);
        }

        return this.associatedBeanNameWithAliases;
    }

    /**
     * @inheritDoc
     */
    @Override
    public void destroy() {
        unregisterAliases(getAssociatedBeanNameWithAliases());
    }

    /**
     * Attempts to use a single, existing Spring {@link BeanFactory} from the registry based on
     * the {@link #setBeanName(String)} beanName} property.
     *
     * @return the single Spring {@link BeanFactory} from the registry.
     * @throws IllegalArgumentException if more than Spring {@link BeanFactory} is registered.
     * @throws IllegalStateException if the {@link BeanFactory} with the associated
     * {@link #setBeanName(String) beanName} is not found.
     * @see org.springframework.beans.factory.BeanFactory
     * @see #getAssociatedBeanName()
     * @see #useBeanFactory(String)
     */
    public BeanFactory useBeanFactory() {
        return useBeanFactory(getAssociatedBeanName());
    }

    /**
     * Attempts to use the Spring {@link BeanFactory} idenified by the given {@code beanFactoryKey}.
     *
     * @param beanFactoryKey {@link String} containing the key used to lookup the Spring {@link BeanFactory}.
     * @return the Spring {@link BeanFactory} for the given {@code beanFactoryKey}.
     * @throws IllegalArgumentException if a Spring {@link BeanFactory} could not be found for {@code beanFactoryKey}.
     * @throws IllegalStateException if {@literal useBeanFactoryLocator} was not configured.
     * @see org.springframework.beans.factory.BeanFactory
     * @see BeanFactoryReference#newBeanFactoryReference(BeanFactory)
     * @see #resolveBeanFactory(String)
     * @see #resolveSingleBeanFactory()
     */
    public BeanFactory useBeanFactory(String beanFactoryKey) {
        return newBeanFactoryReference(StringUtils.hasText(beanFactoryKey) ? resolveBeanFactory(beanFactoryKey)
                : resolveSingleBeanFactory()).get();
    }

    /**
     * @inheritDoc
     */
    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    /**
     * Returns a reference to the {@link BeanFactory} managed by this {@link GemfireBeanFactoryLocator} instance;
     * Might be {@literal null} if this {@link GemfireBeanFactoryLocator} is just used to lookup
     * an existing {@link BeanFactory} reference.
     *
     * @return the managed {@link BeanFactory} reference.
     * @see org.springframework.beans.factory.BeanFactory
     */
    protected BeanFactory getBeanFactory() {
        return this.beanFactory;
    }

    /**
     * @inheritDoc
     */
    @Override
    public void setBeanName(String name) {
        this.associatedBeanName = name;
    }

    /**
     * Gets the name of the Spring bean associated with the Spring {@link BeanFactory} that possibly created the bean.
     *
     * @return a {@link String} containing the name of the Spring bean associated with the Spring {@link BeanFactory}.
     * @see #setBeanFactory(BeanFactory)
     */
    protected String getAssociatedBeanName() {
        return this.associatedBeanName;
    }

    /**
     * Returns a {@link Set} of all names and aliases assigned to the Spring bean that is associated with
     * the Spring {@link BeanFactory}.
     *
     * @return a {@link Set} containing all the names and aliases assigned to the Spring bean associated with
     * the Spring {@link BeanFactory}.
     * @see #setBeanName(String)
     */
    protected Set<String> getAssociatedBeanNameWithAliases() {
        return Collections.unmodifiableSet(nullSafeSet(this.associatedBeanNameWithAliases));
    }

    /**
     * Builder method to set the bean name used by this locator to lookup a Spring {@link BeanFactory}.
     *
     * @param beanName {@link String} containing the bean name to set on this locator.
     * @return this {@link GemfireBeanFactoryLocator}.
     * @see #setBeanName(String)
     */
    public GemfireBeanFactoryLocator withBeanName(String beanName) {
        setBeanName(beanName);
        return this;
    }

    /**
     * Reference holder storing a reference to a Spring {@link BeanFactory}.
     *
     * @see org.springframework.beans.factory.BeanFactory
     */
    protected static class BeanFactoryReference {

        protected static final String UNINITIALIZED_BEAN_FACTORY_REFERENCE_MESSAGE = "A BeanFactory was not initialized; Please verify the useBeanFactoryLocator property was properly set";

        private final AtomicReference<BeanFactory> beanFactory = new AtomicReference<>(null);

        /**
         * Factory method to construct an instance of {@link BeanFactoryReference} initialized
         * with the given {@link BeanFactory}.
         *
         * @param beanFactory {@link BeanFactory} reference to store.
         * @return a new instance of {@link BeanFactoryReference} initialized with the given {@link BeanFactory}.
         * @see org.springframework.beans.factory.BeanFactory
         * @see #GemfireBeanFactoryLocator.BeanFactoryReference(BeanFactory)
         */
        protected static BeanFactoryReference newBeanFactoryReference(BeanFactory beanFactory) {
            return new BeanFactoryReference(beanFactory);
        }

        /**
         * Constructs an instance of {@link BeanFactoryReference} initialized with the given {@link BeanFactory}.
         *
         * @param beanFactory {@link BeanFactory} reference to store; may be {@literal null}.
         * @see org.springframework.beans.factory.BeanFactory
         */
        protected BeanFactoryReference(BeanFactory beanFactory) {
            this.beanFactory.set(beanFactory);
        }

        /**
         * Returns the reference to the Spring {@link BeanFactory}.
         *
         * @return a reference to the Spring {@link BeanFactory}.
         * @see org.springframework.beans.factory.BeanFactory
         */
        public BeanFactory get() {

            BeanFactory beanFactory = this.beanFactory.get();

            Assert.state(beanFactory != null, UNINITIALIZED_BEAN_FACTORY_REFERENCE_MESSAGE);

            return beanFactory;
        }

        /**
         * Releases the stored reference to the Spring {@link BeanFactory}.
         */
        public void release() {
            this.beanFactory.set(null);
        }
    }
}