org.springframework.context.index.CandidateComponentsIndexLoader.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.context.index.CandidateComponentsIndexLoader.java

Source

/*
 * Copyright 2002-2017 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
 *
 *      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.springframework.context.index;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ConcurrentMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.core.SpringProperties;
import org.springframework.core.io.UrlResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.ConcurrentReferenceHashMap;

/**
 * Candidate components index loading mechanism for internal use within the framework.
 *
 * @author Stephane Nicoll
 * @since 5.0
 */
public class CandidateComponentsIndexLoader {

    /**
     * The location to look for components.
     * <p>Can be present in multiple JAR files.
     */
    public static final String COMPONENTS_RESOURCE_LOCATION = "META-INF/spring.components";

    /**
     * System property that instructs Spring to ignore the index, i.e.
     * to always return {@code null} from {@link #loadIndex(ClassLoader)}.
     * <p>The default is "false", allowing for regular use of the index. Switching this
     * flag to {@code true} fulfills a corner case scenario when an index is partially
     * available for some libraries (or use cases) but couldn't be built for the whole
     * application. In this case, the application context fallbacks to a regular
     * classpath arrangement (i.e. as no index was present at all).
     */
    public static final String IGNORE_INDEX = "spring.index.ignore";

    private static final boolean shouldIgnoreIndex = SpringProperties.getFlag(IGNORE_INDEX);

    private static final Log logger = LogFactory.getLog(CandidateComponentsIndexLoader.class);

    private static final ConcurrentMap<ClassLoader, CandidateComponentsIndex> cache = new ConcurrentReferenceHashMap<>();

    /**
     * Load and instantiate the {@link CandidateComponentsIndex} from
     * {@value #COMPONENTS_RESOURCE_LOCATION}, using the given class loader. If no
     * index is available, return {@code null}.
     * @param classLoader the ClassLoader to use for loading (can be {@code null} to use the default)
     * @return the index to use or {@code null} if no index was found
     * @throws IllegalArgumentException if any module index cannot
     * be loaded or if an error occurs while creating {@link CandidateComponentsIndex}
     */
    @Nullable
    public static CandidateComponentsIndex loadIndex(@Nullable ClassLoader classLoader) {
        ClassLoader classLoaderToUse = classLoader;
        if (classLoaderToUse == null) {
            classLoaderToUse = CandidateComponentsIndexLoader.class.getClassLoader();
        }
        return cache.computeIfAbsent(classLoaderToUse, CandidateComponentsIndexLoader::doLoadIndex);
    }

    @Nullable
    private static CandidateComponentsIndex doLoadIndex(ClassLoader classLoader) {
        if (shouldIgnoreIndex) {
            return null;
        }

        try {
            Enumeration<URL> urls = classLoader.getResources(COMPONENTS_RESOURCE_LOCATION);
            if (!urls.hasMoreElements()) {
                return null;
            }
            List<Properties> result = new ArrayList<>();
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
                result.add(properties);
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + result.size() + "] index(es)");
            }
            int totalCount = result.stream().mapToInt(Properties::size).sum();
            return (totalCount > 0 ? new CandidateComponentsIndex(result) : null);
        } catch (IOException ex) {
            throw new IllegalStateException(
                    "Unable to load indexes from location [" + COMPONENTS_RESOURCE_LOCATION + "]", ex);
        }
    }

}