Home / Class/ SpringIterableConfigurationPropertySource Class — spring-boot Architecture

SpringIterableConfigurationPropertySource Class — spring-boot Architecture

Architecture documentation for the SpringIterableConfigurationPropertySource class in SpringIterableConfigurationPropertySource.java from the spring-boot codebase.

Entity Profile

Relationship Graph

Source Code

core/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringIterableConfigurationPropertySource.java lines 57–408

class SpringIterableConfigurationPropertySource extends SpringConfigurationPropertySource
		implements IterableConfigurationPropertySource, CachingConfigurationPropertySource {

	private final BiPredicate<ConfigurationPropertyName, ConfigurationPropertyName> ancestorOfCheck;

	private final SoftReferenceConfigurationPropertyCache<Cache> cache;

	private volatile @Nullable ConfigurationPropertyName @Nullable [] configurationPropertyNames;

	private final @Nullable Map<ConfigurationPropertyName, ConfigurationPropertyState> containsDescendantOfCache;

	SpringIterableConfigurationPropertySource(EnumerablePropertySource<?> propertySource,
			boolean systemEnvironmentSource, PropertyMapper... mappers) {
		super(propertySource, systemEnvironmentSource, mappers);
		assertEnumerablePropertySource();
		boolean immutable = isImmutablePropertySource();
		this.ancestorOfCheck = getAncestorOfCheck(mappers);
		this.cache = new SoftReferenceConfigurationPropertyCache<>(immutable);
		this.containsDescendantOfCache = (!systemEnvironmentSource) ? null : new ConcurrentReferenceHashMap<>();
	}

	private BiPredicate<ConfigurationPropertyName, ConfigurationPropertyName> getAncestorOfCheck(
			PropertyMapper[] mappers) {
		BiPredicate<ConfigurationPropertyName, ConfigurationPropertyName> ancestorOfCheck = mappers[0]
			.getAncestorOfCheck();
		for (int i = 1; i < mappers.length; i++) {
			ancestorOfCheck = ancestorOfCheck.or(mappers[i].getAncestorOfCheck());
		}
		return ancestorOfCheck;
	}

	private void assertEnumerablePropertySource() {
		if (getPropertySource() instanceof MapPropertySource mapSource) {
			try {
				mapSource.getSource().size();
			}
			catch (UnsupportedOperationException ex) {
				throw new IllegalArgumentException("PropertySource must be fully enumerable");
			}
		}
	}

	@Override
	public ConfigurationPropertyCaching getCaching() {
		return this.cache;
	}

	@Override
	public @Nullable ConfigurationProperty getConfigurationProperty(@Nullable ConfigurationPropertyName name) {
		if (name == null) {
			return null;
		}
		ConfigurationProperty configurationProperty = super.getConfigurationProperty(name);
		if (configurationProperty != null) {
			return configurationProperty;
		}
		for (String candidate : getCache().getMapped(name)) {
			Object value = getPropertySourceProperty(candidate);
			if (value != null) {
				Origin origin = PropertySourceOrigin.get(getPropertySource(), candidate);
				return ConfigurationProperty.of(this, name, value, origin);
			}
		}
		return null;
	}

	@Override
	protected @Nullable Object getSystemEnvironmentProperty(Map<String, Object> systemEnvironment, String name) {
		return getCache().getSystemEnvironmentProperty(name);
	}

	@Override
	public Stream<ConfigurationPropertyName> stream() {
		@Nullable ConfigurationPropertyName[] names = getConfigurationPropertyNames();
		return Arrays.stream(names).filter(Objects::nonNull);
	}

	@Override
	public Iterator<ConfigurationPropertyName> iterator() {
		return new ConfigurationPropertyNamesIterator(getConfigurationPropertyNames());
	}

	@Override
	public ConfigurationPropertyState containsDescendantOf(ConfigurationPropertyName name) {
		ConfigurationPropertyState result = super.containsDescendantOf(name);
		if (result != ConfigurationPropertyState.UNKNOWN) {
			return result;
		}
		if (this.ancestorOfCheck == PropertyMapper.DEFAULT_ANCESTOR_OF_CHECK) {
			Set<ConfigurationPropertyName> descendants = getCache().getDescendants();
			if (descendants != null) {
				if (name.isEmpty() && !descendants.isEmpty()) {
					return ConfigurationPropertyState.PRESENT;
				}
				return !descendants.contains(name) ? ConfigurationPropertyState.ABSENT
						: ConfigurationPropertyState.PRESENT;
			}
		}
		result = (this.containsDescendantOfCache != null) ? this.containsDescendantOfCache.get(name) : null;
		if (result == null) {
			result = (!ancestorOfCheck(name)) ? ConfigurationPropertyState.ABSENT : ConfigurationPropertyState.PRESENT;
			if (this.containsDescendantOfCache != null) {
				this.containsDescendantOfCache.put(name, result);
			}
		}
		return result;
	}

	private boolean ancestorOfCheck(ConfigurationPropertyName name) {
		@Nullable ConfigurationPropertyName[] candidates = getConfigurationPropertyNames();
		for (ConfigurationPropertyName candidate : candidates) {
			if (candidate != null && this.ancestorOfCheck.test(name, candidate)) {
				return true;
			}
		}
		return false;
	}

	@Nullable ConfigurationPropertyName[] getConfigurationPropertyNames() {
		if (!isImmutablePropertySource()) {
			return getCache().getConfigurationPropertyNames(getPropertySource().getPropertyNames());
		}
		@Nullable ConfigurationPropertyName[] configurationPropertyNames = this.configurationPropertyNames;
		if (configurationPropertyNames == null) {
			configurationPropertyNames = getCache()
				.getConfigurationPropertyNames(getPropertySource().getPropertyNames());
			this.configurationPropertyNames = configurationPropertyNames;
		}
		return configurationPropertyNames;
	}

	private Cache getCache() {
		return this.cache.get(this::createCache, this::updateCache);
	}

	private Cache createCache() {
		boolean immutable = isImmutablePropertySource();
		boolean captureDescendants = this.ancestorOfCheck == PropertyMapper.DEFAULT_ANCESTOR_OF_CHECK;
		return new Cache(getMappers(), immutable, captureDescendants, isSystemEnvironmentSource());
	}

	private Cache updateCache(Cache cache) {
		cache.update(getPropertySource());
		return cache;
	}

	boolean isImmutablePropertySource() {
		EnumerablePropertySource<?> source = getPropertySource();
		if (source instanceof PropertySourceInfo propertySourceInfo) {
			return propertySourceInfo.isImmutable();
		}
		if (StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME.equals(source.getName())) {
			return source.getSource() == System.getenv();
		}
		return false;
	}

	@Override
	protected EnumerablePropertySource<?> getPropertySource() {
		return (EnumerablePropertySource<?>) super.getPropertySource();
	}

	private static class Cache {

		private static final ConfigurationPropertyName[] EMPTY_NAMES_ARRAY = {};

		private final PropertyMapper[] mappers;

		private final boolean immutable;

		private final boolean captureDescendants;

		private final boolean systemEnvironmentSource;

		private volatile @Nullable Data data;

		Cache(PropertyMapper[] mappers, boolean immutable, boolean captureDescendants,
				boolean systemEnvironmentSource) {
			this.mappers = mappers;
			this.immutable = immutable;
			this.captureDescendants = captureDescendants;
			this.systemEnvironmentSource = systemEnvironmentSource;
		}

		void update(EnumerablePropertySource<?> propertySource) {
			if (this.data == null || !this.immutable) {
				int count = 0;
				while (true) {
					try {
						tryUpdate(propertySource);
						return;
					}
					catch (ConcurrentModificationException ex) {
						if (count++ > 10) {
							throw ex;
						}
					}
				}
			}
		}

		private void tryUpdate(EnumerablePropertySource<?> propertySource) {
			Data data = this.data;
			String[] lastUpdated = (data != null) ? data.lastUpdated() : null;
			String[] propertyNames = propertySource.getPropertyNames();
			if (lastUpdated != null && Arrays.equals(lastUpdated, propertyNames)) {
				return;
			}
			int size = propertyNames.length;
			Map<ConfigurationPropertyName, Set<String>> mappings = cloneOrCreate(
					(data != null) ? data.mappings() : null, size);
			Map<String, ConfigurationPropertyName> reverseMappings = cloneOrCreate(
					(data != null) ? data.reverseMappings() : null, size);
			Set<ConfigurationPropertyName> descendants = (!this.captureDescendants) ? null : new HashSet<>();
			Map<String, Object> systemEnvironmentCopy = (!this.systemEnvironmentSource) ? null
					: copySource(propertySource);
			for (PropertyMapper propertyMapper : this.mappers) {
				for (String propertyName : propertyNames) {
					if (!reverseMappings.containsKey(propertyName)) {
						ConfigurationPropertyName configurationPropertyName = propertyMapper.map(propertyName);
						if (configurationPropertyName != null && !configurationPropertyName.isEmpty()) {
							add(mappings, configurationPropertyName, propertyName);
							reverseMappings.put(propertyName, configurationPropertyName);
						}
					}
				}
			}
			for (String propertyName : propertyNames) {
				addParents(descendants, reverseMappings.get(propertyName));
			}
			ConfigurationPropertyName[] configurationPropertyNames = this.immutable
					? reverseMappings.values().toArray(new ConfigurationPropertyName[0]) : null;
			lastUpdated = this.immutable ? null : propertyNames;
			this.data = new Data(mappings, reverseMappings, descendants, configurationPropertyNames,
					systemEnvironmentCopy, lastUpdated);
		}

		@SuppressWarnings("unchecked")
		private HashMap<String, Object> copySource(EnumerablePropertySource<?> propertySource) {
			return new HashMap<>((Map<String, Object>) propertySource.getSource());
		}

		private <K, V> Map<K, V> cloneOrCreate(@Nullable Map<K, V> source, int size) {
			return (source != null) ? new LinkedHashMap<>(source) : new LinkedHashMap<>(size);
		}

		private void addParents(@Nullable Set<ConfigurationPropertyName> descendants,
				@Nullable ConfigurationPropertyName name) {
			if (descendants == null || name == null || name.isEmpty()) {
				return;
			}
			ConfigurationPropertyName parent = name.getParent();
			while (!parent.isEmpty()) {
				if (!descendants.add(parent)) {
					return;
				}
				parent = parent.getParent();
			}
		}

		private <K, T> void add(Map<K, Set<T>> map, K key, T value) {
			map.computeIfAbsent(key, (k) -> new HashSet<>()).add(value);
		}

		Set<String> getMapped(ConfigurationPropertyName configurationPropertyName) {
			Data data = this.data;
			Assert.state(data != null, "'data' must not be null");
			return data.mappings().getOrDefault(configurationPropertyName, Collections.emptySet());
		}

		@Nullable ConfigurationPropertyName[] getConfigurationPropertyNames(String[] propertyNames) {
			Data data = this.data;
			Assert.state(data != null, "'data' must not be null");
			@Nullable ConfigurationPropertyName[] names = data.configurationPropertyNames();
			if (names != null) {
				return names;
			}
			Map<String, ConfigurationPropertyName> reverseMappings = data.reverseMappings();
			if (reverseMappings == null || reverseMappings.isEmpty()) {
				return EMPTY_NAMES_ARRAY;
			}
			names = new ConfigurationPropertyName[propertyNames.length];
			for (int i = 0; i < propertyNames.length; i++) {
				names[i] = reverseMappings.get(propertyNames[i]);
			}
			return names;
		}

		@Nullable Set<ConfigurationPropertyName> getDescendants() {
			Data data = this.data;
			Assert.state(data != null, "'data' must not be null");
			return data.descendants();
		}

		@Nullable Object getSystemEnvironmentProperty(String name) {
			Data data = this.data;
			Assert.state(data != null, "'data' must not be null");
			Map<String, Object> systemEnvironmentCopy = data.systemEnvironmentCopy();
			Assert.state(systemEnvironmentCopy != null, "'systemEnvironmentCopy' must not be null");
			return systemEnvironmentCopy.get(name);
		}

		private record Data(Map<ConfigurationPropertyName, Set<String>> mappings,
				Map<String, ConfigurationPropertyName> reverseMappings,
				@Nullable Set<ConfigurationPropertyName> descendants,
				ConfigurationPropertyName @Nullable [] configurationPropertyNames,
				@Nullable Map<String, Object> systemEnvironmentCopy, String @Nullable [] lastUpdated) {

		}

	}

	/**
	 * ConfigurationPropertyNames iterator backed by an array.
	 */
	private static class ConfigurationPropertyNamesIterator implements Iterator<ConfigurationPropertyName> {

		private final @Nullable ConfigurationPropertyName[] names;

		private int index;

		ConfigurationPropertyNamesIterator(@Nullable ConfigurationPropertyName[] names) {
			this.names = names;
		}

		@Override
		public boolean hasNext() {
			skipNulls();
			return this.index < this.names.length;
		}

		@Override
		public @Nullable ConfigurationPropertyName next() {
			skipNulls();
			if (this.index >= this.names.length) {
				throw new NoSuchElementException();
			}
			return this.names[this.index++];
		}

		private void skipNulls() {
			while (this.index < this.names.length) {
				if (this.names[this.index] != null) {
					return;
				}
				this.index++;
			}
		}

	}

}

Domain

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free