• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

TAKETODAY / today-infrastructure / 18901940452

29 Oct 2025 08:40AM UTC coverage: 83.385% (-0.01%) from 83.397%
18901940452

push

github

TAKETODAY
:art:

60820 of 78019 branches covered (77.96%)

Branch coverage included in aggregate %.

143823 of 167400 relevant lines covered (85.92%)

3.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.0
today-context/src/main/java/infra/context/index/CandidateComponentsIndexLoader.java
1
/*
2
 * Copyright 2017 - 2025 the original author or authors.
3
 *
4
 * This program is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see [https://www.gnu.org/licenses/]
16
 */
17

18
package infra.context.index;
19

20
import org.jspecify.annotations.Nullable;
21

22
import java.io.IOException;
23
import java.net.URL;
24
import java.util.ArrayList;
25
import java.util.Enumeration;
26
import java.util.List;
27
import java.util.Properties;
28
import java.util.concurrent.ConcurrentMap;
29

30
import infra.core.io.PropertiesUtils;
31
import infra.core.io.UrlResource;
32
import infra.lang.TodayStrategies;
33
import infra.logging.Logger;
34
import infra.logging.LoggerFactory;
35
import infra.util.ConcurrentReferenceHashMap;
36

37
/**
38
 * Candidate components index loading mechanism for internal use within the framework.
39
 *
40
 * @author Stephane Nicoll
41
 * @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
42
 * @since 4.0 2021/12/9 21:35
43
 */
44
public final class CandidateComponentsIndexLoader {
45

46
  /**
47
   * The location to look for components.
48
   * <p>Can be present in multiple JAR files.
49
   */
50
  public static final String COMPONENTS_RESOURCE_LOCATION = "META-INF/today.components";
51

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

63
  private static final boolean shouldIgnoreIndex = TodayStrategies.getFlag(IGNORE_INDEX);
3✔
64

65
  private static final Logger log = LoggerFactory.getLogger(CandidateComponentsIndexLoader.class);
3✔
66

67
  private static final ConcurrentMap<ClassLoader, CandidateComponentsIndex> cache =
5✔
68
          new ConcurrentReferenceHashMap<>();
69

70
  private CandidateComponentsIndexLoader() {
71
  }
72

73
  /**
74
   * Load and instantiate the {@link CandidateComponentsIndex} from
75
   * {@value #COMPONENTS_RESOURCE_LOCATION}, using the given class loader. If no
76
   * index is available, return {@code null}.
77
   *
78
   * @param classLoader the ClassLoader to use for loading (can be {@code null} to use the default)
79
   * @return the index to use or {@code null} if no index was found
80
   * @throws IllegalArgumentException if any module index cannot
81
   * be loaded or if an error occurs while creating {@link CandidateComponentsIndex}
82
   */
83
  public static @Nullable CandidateComponentsIndex loadIndex(@Nullable ClassLoader classLoader) {
84
    ClassLoader classLoaderToUse = classLoader;
2✔
85
    if (classLoaderToUse == null) {
2✔
86
      classLoaderToUse = CandidateComponentsIndexLoader.class.getClassLoader();
3✔
87
    }
88
    return cache.computeIfAbsent(classLoaderToUse, CandidateComponentsIndexLoader::doLoadIndex);
6✔
89
  }
90

91
  private static @Nullable CandidateComponentsIndex doLoadIndex(ClassLoader classLoader) {
92
    if (shouldIgnoreIndex) {
2!
93
      return null;
×
94
    }
95

96
    try {
97
      Enumeration<URL> urls = classLoader.getResources(COMPONENTS_RESOURCE_LOCATION);
4✔
98
      if (!urls.hasMoreElements()) {
3✔
99
        return null;
2✔
100
      }
101
      List<Properties> result = new ArrayList<>();
4✔
102
      while (urls.hasMoreElements()) {
3✔
103
        URL url = urls.nextElement();
4✔
104
        Properties properties = PropertiesUtils.loadProperties(new UrlResource(url));
6✔
105
        result.add(properties);
4✔
106
      }
1✔
107
      if (log.isDebugEnabled()) {
3!
108
        log.debug("Loaded {} index(es)", result.size());
×
109
      }
110
      int totalCount = result.stream().mapToInt(Properties::size).sum();
6✔
111
      return totalCount > 0 ? new CandidateComponentsIndex(result) : null;
9✔
112
    }
113
    catch (IOException ex) {
1✔
114
      throw new IllegalStateException("Unable to load indexes from location [%s]".formatted(COMPONENTS_RESOURCE_LOCATION), ex);
13✔
115
    }
116
  }
117

118
  /**
119
   * Programmatically add the given index instance for the given ClassLoader,
120
   * replacing a file-determined index with a programmatically composed index.
121
   * <p>The index instance will usually be pre-populated for AOT runtime setups
122
   * or test scenarios with pre-configured results for runtime-attempted scans.
123
   * Alternatively, it may be empty for it to get populated during AOT processing
124
   * or a test run, for subsequent introspection the index-recorded candidate types.
125
   *
126
   * @param classLoader the ClassLoader to add the index for
127
   * @param index the associated CandidateComponentsIndex instance
128
   * @since 5.0
129
   */
130
  public static void addIndex(ClassLoader classLoader, CandidateComponentsIndex index) {
131
    cache.put(classLoader, index);
5✔
132
  }
1✔
133

134
  /**
135
   * Clear the runtime index cache.
136
   *
137
   * @since 5.0
138
   */
139
  public static void clearCache() {
140
    cache.clear();
2✔
141
  }
1✔
142

143
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc