/*
 * Copyright 2002-2020 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.core.env;

import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;

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

import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.PropertyPlaceholderHelper;
import org.springframework.util.SystemPropertyUtils;

Abstract base class for resolving properties against any underlying source.
Author:Chris Beams, Juergen Hoeller
Since:3.1
/** * Abstract base class for resolving properties against any underlying source. * * @author Chris Beams * @author Juergen Hoeller * @since 3.1 */
public abstract class AbstractPropertyResolver implements ConfigurablePropertyResolver { protected final Log logger = LogFactory.getLog(getClass()); @Nullable private volatile ConfigurableConversionService conversionService; @Nullable private PropertyPlaceholderHelper nonStrictHelper; @Nullable private PropertyPlaceholderHelper strictHelper; private boolean ignoreUnresolvableNestedPlaceholders = false; private String placeholderPrefix = SystemPropertyUtils.PLACEHOLDER_PREFIX; private String placeholderSuffix = SystemPropertyUtils.PLACEHOLDER_SUFFIX; @Nullable private String valueSeparator = SystemPropertyUtils.VALUE_SEPARATOR; private final Set<String> requiredProperties = new LinkedHashSet<>(); @Override public ConfigurableConversionService getConversionService() { // Need to provide an independent DefaultConversionService, not the // shared DefaultConversionService used by PropertySourcesPropertyResolver. ConfigurableConversionService cs = this.conversionService; if (cs == null) { synchronized (this) { cs = this.conversionService; if (cs == null) { cs = new DefaultConversionService(); this.conversionService = cs; } } } return cs; } @Override public void setConversionService(ConfigurableConversionService conversionService) { Assert.notNull(conversionService, "ConversionService must not be null"); this.conversionService = conversionService; }
Set the prefix that placeholders replaced by this resolver must begin with.

The default is "${".

See Also:
  • PLACEHOLDER_PREFIX.PLACEHOLDER_PREFIX
/** * Set the prefix that placeholders replaced by this resolver must begin with. * <p>The default is "${". * @see org.springframework.util.SystemPropertyUtils#PLACEHOLDER_PREFIX */
@Override public void setPlaceholderPrefix(String placeholderPrefix) { Assert.notNull(placeholderPrefix, "'placeholderPrefix' must not be null"); this.placeholderPrefix = placeholderPrefix; }
Set the suffix that placeholders replaced by this resolver must end with.

The default is "}".

See Also:
  • PLACEHOLDER_SUFFIX.PLACEHOLDER_SUFFIX
/** * Set the suffix that placeholders replaced by this resolver must end with. * <p>The default is "}". * @see org.springframework.util.SystemPropertyUtils#PLACEHOLDER_SUFFIX */
@Override public void setPlaceholderSuffix(String placeholderSuffix) { Assert.notNull(placeholderSuffix, "'placeholderSuffix' must not be null"); this.placeholderSuffix = placeholderSuffix; }
Specify the separating character between the placeholders replaced by this resolver and their associated default value, or null if no such special character should be processed as a value separator.

The default is ":".

See Also:
/** * Specify the separating character between the placeholders replaced by this * resolver and their associated default value, or {@code null} if no such * special character should be processed as a value separator. * <p>The default is ":". * @see org.springframework.util.SystemPropertyUtils#VALUE_SEPARATOR */
@Override public void setValueSeparator(@Nullable String valueSeparator) { this.valueSeparator = valueSeparator; }
Set whether to throw an exception when encountering an unresolvable placeholder nested within the value of a given property. A false value indicates strict resolution, i.e. that an exception will be thrown. A true value indicates that unresolvable nested placeholders should be passed through in their unresolved ${...} form.

The default is false.

Since:3.2
/** * Set whether to throw an exception when encountering an unresolvable placeholder * nested within the value of a given property. A {@code false} value indicates strict * resolution, i.e. that an exception will be thrown. A {@code true} value indicates * that unresolvable nested placeholders should be passed through in their unresolved * ${...} form. * <p>The default is {@code false}. * @since 3.2 */
@Override public void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders) { this.ignoreUnresolvableNestedPlaceholders = ignoreUnresolvableNestedPlaceholders; } @Override public void setRequiredProperties(String... requiredProperties) { Collections.addAll(this.requiredProperties, requiredProperties); } @Override public void validateRequiredProperties() { MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException(); for (String key : this.requiredProperties) { if (this.getProperty(key) == null) { ex.addMissingRequiredProperty(key); } } if (!ex.getMissingRequiredProperties().isEmpty()) { throw ex; } } @Override public boolean containsProperty(String key) { return (getProperty(key) != null); } @Override @Nullable public String getProperty(String key) { return getProperty(key, String.class); } @Override public String getProperty(String key, String defaultValue) { String value = getProperty(key); return (value != null ? value : defaultValue); } @Override public <T> T getProperty(String key, Class<T> targetType, T defaultValue) { T value = getProperty(key, targetType); return (value != null ? value : defaultValue); } @Override public String getRequiredProperty(String key) throws IllegalStateException { String value = getProperty(key); if (value == null) { throw new IllegalStateException("Required key '" + key + "' not found"); } return value; } @Override public <T> T getRequiredProperty(String key, Class<T> valueType) throws IllegalStateException { T value = getProperty(key, valueType); if (value == null) { throw new IllegalStateException("Required key '" + key + "' not found"); } return value; } @Override public String resolvePlaceholders(String text) { if (this.nonStrictHelper == null) { this.nonStrictHelper = createPlaceholderHelper(true); } return doResolvePlaceholders(text, this.nonStrictHelper); } @Override public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException { if (this.strictHelper == null) { this.strictHelper = createPlaceholderHelper(false); } return doResolvePlaceholders(text, this.strictHelper); }
Resolve placeholders within the given string, deferring to the value of setIgnoreUnresolvableNestedPlaceholders to determine whether any unresolvable placeholders should raise an exception or be ignored.

Invoked from getProperty and its variants, implicitly resolving nested placeholders. In contrast, resolvePlaceholders and resolveRequiredPlaceholders do not delegate to this method but rather perform their own handling of unresolvable placeholders, as specified by each of those methods.

See Also:
Since:3.2
/** * Resolve placeholders within the given string, deferring to the value of * {@link #setIgnoreUnresolvableNestedPlaceholders} to determine whether any * unresolvable placeholders should raise an exception or be ignored. * <p>Invoked from {@link #getProperty} and its variants, implicitly resolving * nested placeholders. In contrast, {@link #resolvePlaceholders} and * {@link #resolveRequiredPlaceholders} do <i>not</i> delegate * to this method but rather perform their own handling of unresolvable * placeholders, as specified by each of those methods. * @since 3.2 * @see #setIgnoreUnresolvableNestedPlaceholders */
protected String resolveNestedPlaceholders(String value) { if (value.isEmpty()) { return value; } return (this.ignoreUnresolvableNestedPlaceholders ? resolvePlaceholders(value) : resolveRequiredPlaceholders(value)); } private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) { return new PropertyPlaceholderHelper(this.placeholderPrefix, this.placeholderSuffix, this.valueSeparator, ignoreUnresolvablePlaceholders); } private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) { return helper.replacePlaceholders(text, this::getPropertyAsRawString); }
Convert the given value to the specified target type, if necessary.
Params:
  • value – the original property value
  • targetType – the specified target type for property retrieval
Returns:the converted value, or the original value if no conversion is necessary
Since:4.3.5
/** * Convert the given value to the specified target type, if necessary. * @param value the original property value * @param targetType the specified target type for property retrieval * @return the converted value, or the original value if no conversion * is necessary * @since 4.3.5 */
@SuppressWarnings("unchecked") @Nullable protected <T> T convertValueIfNecessary(Object value, @Nullable Class<T> targetType) { if (targetType == null) { return (T) value; } ConversionService conversionServiceToUse = this.conversionService; if (conversionServiceToUse == null) { // Avoid initialization of shared DefaultConversionService if // no standard type conversion is needed in the first place... if (ClassUtils.isAssignableValue(targetType, value)) { return (T) value; } conversionServiceToUse = DefaultConversionService.getSharedInstance(); } return conversionServiceToUse.convert(value, targetType); }
Retrieve the specified property as a raw String, i.e. without resolution of nested placeholders.
Params:
  • key – the property name to resolve
Returns:the property value or null if none found
/** * Retrieve the specified property as a raw String, * i.e. without resolution of nested placeholders. * @param key the property name to resolve * @return the property value or {@code null} if none found */
@Nullable protected abstract String getPropertyAsRawString(String key); }