org.springframework.context.index.CandidateComponentsIndexLoader Maven / Gradle / Ivy
/*
* Copyright 2002-2021 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.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 final class CandidateComponentsIndexLoader {
/**
* The location to look for components.
* 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 components index, i.e.
* to always return {@code null} from {@link #loadIndex(ClassLoader)}.
*
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 though no index were 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 cache =
new ConcurrentReferenceHashMap<>();
private CandidateComponentsIndexLoader() {
}
/**
* 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 urls = classLoader.getResources(COMPONENTS_RESOURCE_LOCATION);
if (!urls.hasMoreElements()) {
return null;
}
List 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);
}
}
}