support ArthasEnvironment. #986

This commit is contained in:
hengyunabc 2019-12-27 15:11:18 +08:00
parent 558137a581
commit f5270aba36
24 changed files with 2474 additions and 0 deletions

View File

@ -134,6 +134,7 @@
<groupId>com.taobao.middleware</groupId>
<artifactId>logger.api</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
@ -161,6 +162,12 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.benf</groupId>
<artifactId>cfr</artifactId>

View File

@ -0,0 +1,226 @@
/*
* Copyright 2002-2018 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 com.taobao.arthas.core.env;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* 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 ConfigurableConversionService conversionService = new DefaultConversionService();
private PropertyPlaceholderHelper nonStrictHelper;
private PropertyPlaceholderHelper strictHelper;
private boolean ignoreUnresolvableNestedPlaceholders = false;
private String placeholderPrefix = SystemPropertyUtils.PLACEHOLDER_PREFIX;
private String placeholderSuffix = SystemPropertyUtils.PLACEHOLDER_SUFFIX;
private String valueSeparator = SystemPropertyUtils.VALUE_SEPARATOR;
private final Set<String> requiredProperties = new LinkedHashSet<String>();
public ConfigurableConversionService getConversionService() {
return this.conversionService;
}
public void setConversionService(ConfigurableConversionService conversionService) {
this.conversionService = conversionService;
}
/**
* 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) {
this.placeholderPrefix = placeholderPrefix;
}
/**
* 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) {
this.placeholderSuffix = placeholderSuffix;
}
/**
* 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(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 {@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) {
for (String key : requiredProperties) {
this.requiredProperties.add(key);
}
}
@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
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
* {@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) {
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, new PropertyPlaceholderHelper.PlaceholderResolver() {
public String resolvePlaceholder(String placeholderName) {
return getPropertyAsRawString(placeholderName);
}
});
}
/**
* 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
*/
protected abstract String getPropertyAsRawString(String key);
}

View File

@ -0,0 +1,122 @@
package com.taobao.arthas.core.env;
import java.security.AccessControlException;
import java.util.Map;
/**
*
* @author hengyunabc 2019-12-27
*
*/
public class ArthasEnvironment implements Environment {
/** System environment property source name: {@value}. */
public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
/** JVM system properties property source name: {@value}. */
public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
private final MutablePropertySources propertySources = new MutablePropertySources();
private final ConfigurablePropertyResolver propertyResolver = new PropertySourcesPropertyResolver(
this.propertySources);
public ArthasEnvironment() {
propertySources
.addLast(new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(
new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
/**
* Add the given property source object with lowest precedence.
*/
public void addLast(PropertySource<?> propertySource) {
this.propertySources.addLast(propertySource);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public Map<String, Object> getSystemProperties() {
try {
return (Map) System.getProperties();
} catch (AccessControlException ex) {
return (Map) new ReadOnlySystemAttributesMap() {
@Override
protected String getSystemAttribute(String attributeName) {
try {
return System.getProperty(attributeName);
} catch (AccessControlException ex) {
return null;
}
}
};
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public Map<String, Object> getSystemEnvironment() {
try {
return (Map) System.getenv();
} catch (AccessControlException ex) {
return (Map) new ReadOnlySystemAttributesMap() {
@Override
protected String getSystemAttribute(String attributeName) {
try {
return System.getenv(attributeName);
} catch (AccessControlException ex) {
return null;
}
}
};
}
}
// ---------------------------------------------------------------------
// Implementation of PropertyResolver interface
// ---------------------------------------------------------------------
@Override
public boolean containsProperty(String key) {
return this.propertyResolver.containsProperty(key);
}
@Override
public String getProperty(String key) {
return this.propertyResolver.getProperty(key);
}
@Override
public String getProperty(String key, String defaultValue) {
return this.propertyResolver.getProperty(key, defaultValue);
}
@Override
public <T> T getProperty(String key, Class<T> targetType) {
return this.propertyResolver.getProperty(key, targetType);
}
@Override
public <T> T getProperty(String key, Class<T> targetType, T defaultValue) {
return this.propertyResolver.getProperty(key, targetType, defaultValue);
}
@Override
public String getRequiredProperty(String key) throws IllegalStateException {
return this.propertyResolver.getRequiredProperty(key);
}
@Override
public <T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException {
return this.propertyResolver.getRequiredProperty(key, targetType);
}
@Override
public String resolvePlaceholders(String text) {
return this.propertyResolver.resolvePlaceholders(text);
}
@Override
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
return this.propertyResolver.resolveRequiredPlaceholders(text);
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright 2002-2011 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 com.taobao.arthas.core.env;
/**
* Configuration interface to be implemented by most if not all
* {@link ConversionService} types. Consolidates the read-only operations
* exposed by {@link ConversionService} and the mutating operations of
* {@link ConverterRegistry} to allow for convenient ad-hoc addition and removal
* of {@link org.springframework.core.convert.converter.Converter Converters}
* through. The latter is particularly useful when working against a
* {@link org.springframework.core.env.ConfigurableEnvironment
* ConfigurableEnvironment} instance in application context bootstrapping code.
*
* @author Chris Beams
* @since 3.1
* @see org.springframework.core.env.ConfigurablePropertyResolver#getConversionService()
* @see org.springframework.core.env.ConfigurableEnvironment
* @see org.springframework.context.ConfigurableApplicationContext#getEnvironment()
*/
public interface ConfigurableConversionService extends ConversionService {
}

View File

@ -0,0 +1,111 @@
/*
* Copyright 2002-2016 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 com.taobao.arthas.core.env;
/**
* Configuration interface to be implemented by most if not all
* {@link PropertyResolver} types. Provides facilities for accessing and
* customizing the {@link org.springframework.core.convert.ConversionService
* ConversionService} used when converting property values from one type to
* another.
*
* @author Chris Beams
* @since 3.1
*/
public interface ConfigurablePropertyResolver extends PropertyResolver {
/**
* Return the {@link ConfigurableConversionService} used when performing type
* conversions on properties.
* <p>
* The configurable nature of the returned conversion service allows for the
* convenient addition and removal of individual {@code Converter} instances:
*
* <pre class="code">
* ConfigurableConversionService cs = env.getConversionService();
* cs.addConverter(new FooConverter());
* </pre>
*
* @see PropertyResolver#getProperty(String, Class)
* @see org.springframework.core.convert.converter.ConverterRegistry#addConverter
*/
ConfigurableConversionService getConversionService();
/**
* Set the {@link ConfigurableConversionService} to be used when performing type
* conversions on properties.
* <p>
* <strong>Note:</strong> as an alternative to fully replacing the
* {@code ConversionService}, consider adding or removing individual
* {@code Converter} instances by drilling into {@link #getConversionService()}
* and calling methods such as {@code #addConverter}.
*
* @see PropertyResolver#getProperty(String, Class)
* @see #getConversionService()
* @see org.springframework.core.convert.converter.ConverterRegistry#addConverter
*/
void setConversionService(ConfigurableConversionService conversionService);
/**
* Set the prefix that placeholders replaced by this resolver must begin with.
*/
void setPlaceholderPrefix(String placeholderPrefix);
/**
* Set the suffix that placeholders replaced by this resolver must end with.
*/
void setPlaceholderSuffix(String placeholderSuffix);
/**
* 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.
*/
void setValueSeparator(String valueSeparator);
/**
* 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>
* Implementations of {@link #getProperty(String)} and its variants must inspect
* the value set here to determine correct behavior when property values contain
* unresolvable placeholders.
*
* @since 3.2
*/
void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders);
/**
* Specify which properties must be present, to be verified by
* {@link #validateRequiredProperties()}.
*/
void setRequiredProperties(String... requiredProperties);
/**
* Validate that each of the properties specified by
* {@link #setRequiredProperties} is present and resolves to a non-{@code null}
* value.
*
* @throws MissingRequiredPropertiesException if any of the required properties
* are not resolvable.
*/
void validateRequiredProperties() throws MissingRequiredPropertiesException;
}

View File

@ -0,0 +1,63 @@
/*
* Copyright 2002-2016 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 com.taobao.arthas.core.env;
/**
* A service interface for type conversion. This is the entry point into the
* convert system. Call {@link #convert(Object, Class)} to perform a thread-safe
* type conversion using this system.
*
* @author Keith Donald
* @author Phillip Webb
* @since 3.0
*/
public interface ConversionService {
/**
* Return {@code true} if objects of {@code sourceType} can be converted to the
* {@code targetType}.
* <p>
* If this method returns {@code true}, it means {@link #convert(Object, Class)}
* is capable of converting an instance of {@code sourceType} to
* {@code targetType}.
* <p>
* Special note on collections, arrays, and maps types: For conversion between
* collection, array, and map types, this method will return {@code true} even
* though a convert invocation may still generate a {@link ConversionException}
* if the underlying elements are not convertible. Callers are expected to
* handle this exceptional case when working with collections and maps.
*
* @param sourceType the source type to convert from (may be {@code null} if
* source is {@code null})
* @param targetType the target type to convert to (required)
* @return {@code true} if a conversion can be performed, {@code false} if not
* @throws IllegalArgumentException if {@code targetType} is {@code null}
*/
boolean canConvert(Class<?> sourceType, Class<?> targetType);
/**
* Convert the given {@code source} to the specified {@code targetType}.
*
* @param source the source object to convert (may be {@code null})
* @param targetType the target type to convert to (required)
* @return the converted object, an instance of targetType
* @throws ConversionException if a conversion exception occurred
* @throws IllegalArgumentException if targetType is {@code null}
*/
<T> T convert(Object source, Class<T> targetType);
}

View File

@ -0,0 +1,17 @@
package com.taobao.arthas.core.env;
public class DefaultConversionService implements ConfigurableConversionService {
@Override
public boolean canConvert(Class<?> sourceType, Class<?> targetType) {
// TODO Auto-generated method stub
return true;
}
@Override
public <T> T convert(Object source, Class<T> targetType) {
// TODO Auto-generated method stub
return (T) source;
}
}

View File

@ -0,0 +1,85 @@
/*
* Copyright 2002-2018 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 com.taobao.arthas.core.env;
/**
* A {@link PropertySource} implementation capable of interrogating its
* underlying source object to enumerate all possible property name/value pairs.
* Exposes the {@link #getPropertyNames()} method to allow callers to introspect
* available properties without having to access the underlying source object.
* This also facilitates a more efficient implementation of
* {@link #containsProperty(String)}, in that it can call
* {@link #getPropertyNames()} and iterate through the returned array rather
* than attempting a call to {@link #getProperty(String)} which may be more
* expensive. Implementations may consider caching the result of
* {@link #getPropertyNames()} to fully exploit this performance opportunity.
*
* <p>
* Most framework-provided {@code PropertySource} implementations are
* enumerable; a counter-example would be {@code JndiPropertySource} where, due
* to the nature of JNDI it is not possible to determine all possible property
* names at any given time; rather it is only possible to try to access a
* property (via {@link #getProperty(String)}) in order to evaluate whether it
* is present or not.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1
* @param <T> the source type
*/
public abstract class EnumerablePropertySource<T> extends PropertySource<T> {
public EnumerablePropertySource(String name, T source) {
super(name, source);
}
protected EnumerablePropertySource(String name) {
super(name);
}
/**
* Return whether this {@code PropertySource} contains a property with the given
* name.
* <p>
* This implementation checks for the presence of the given name within the
* {@link #getPropertyNames()} array.
*
* @param name the name of the property to find
*/
@Override
public boolean containsProperty(String name) {
String[] propertyNames = getPropertyNames();
if (propertyNames == null) {
return false;
}
for (String temp : propertyNames) {
if (temp.equals(name)) {
return true;
}
}
return false;
}
/**
* Return the names of all properties contained by the {@linkplain #getSource()
* source} object (never {@code null}).
*/
public abstract String[] getPropertyNames();
}

View File

@ -0,0 +1,5 @@
package com.taobao.arthas.core.env;
public interface Environment extends PropertyResolver {
}

View File

@ -0,0 +1,52 @@
/*
* Copyright 2002-2014 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 com.taobao.arthas.core.env;
import java.util.Map;
import org.apache.logging.log4j.util.PropertiesPropertySource;
/**
* {@link PropertySource} that reads keys and values from a {@code Map} object.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1
* @see PropertiesPropertySource
*/
public class MapPropertySource extends EnumerablePropertySource<Map<String, Object>> {
public MapPropertySource(String name, Map<String, Object> source) {
super(name, source);
}
@Override
public Object getProperty(String name) {
return this.source.get(name);
}
@Override
public boolean containsProperty(String name) {
return this.source.containsKey(name);
}
@Override
public String[] getPropertyNames() {
return this.source.keySet().toArray(new String[0]);
}
}

View File

@ -0,0 +1,57 @@
/*
* 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
*
* 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 com.taobao.arthas.core.env;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* Exception thrown when required properties are not found.
*
* @author Chris Beams
* @since 3.1
* @see ConfigurablePropertyResolver#setRequiredProperties(String...)
* @see ConfigurablePropertyResolver#validateRequiredProperties()
* @see org.springframework.context.support.AbstractApplicationContext#prepareRefresh()
*/
@SuppressWarnings("serial")
public class MissingRequiredPropertiesException extends IllegalStateException {
private final Set<String> missingRequiredProperties = new LinkedHashSet<String>();
void addMissingRequiredProperty(String key) {
this.missingRequiredProperties.add(key);
}
@Override
public String getMessage() {
return "The following properties were declared as required but could not be resolved: "
+ getMissingRequiredProperties();
}
/**
* Return the set of properties marked as required but not present upon
* validation.
*
* @see ConfigurablePropertyResolver#setRequiredProperties(String...)
* @see ConfigurablePropertyResolver#validateRequiredProperties()
*/
public Set<String> getMissingRequiredProperties() {
return this.missingRequiredProperties;
}
}

View File

@ -0,0 +1,306 @@
/*
* Copyright 2002-2014 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 com.taobao.arthas.core.env;
import java.util.Iterator;
import java.util.LinkedList;
/**
* Default implementation of the {@link PropertySources} interface. Allows
* manipulation of contained property sources and provides a constructor for
* copying an existing {@code PropertySources} instance.
*
* <p>
* Where <em>precedence</em> is mentioned in methods such as {@link #addFirst}
* and {@link #addLast}, this is with regard to the order in which property
* sources will be searched when resolving a given property with a
* {@link PropertyResolver}.
*
* @author Chris Beams
* @since 3.1
* @see PropertySourcesPropertyResolver
*/
public class MutablePropertySources implements PropertySources {
static final String NON_EXISTENT_PROPERTY_SOURCE_MESSAGE = "PropertySource named [%s] does not exist";
static final String ILLEGAL_RELATIVE_ADDITION_MESSAGE = "PropertySource named [%s] cannot be added relative to itself";
private final LinkedList<PropertySource<?>> propertySourceList = new LinkedList<PropertySource<?>>();
/**
* Create a new {@link MutablePropertySources} object.
*/
public MutablePropertySources() {
}
/**
* Create a new {@code MutablePropertySources} from the given propertySources
* object, preserving the original order of contained {@code PropertySource}
* objects.
*/
public MutablePropertySources(PropertySources propertySources) {
this();
for (PropertySource<?> propertySource : propertySources) {
this.addLast(propertySource);
}
}
public boolean contains(String name) {
return this.propertySourceList.contains(PropertySource.named(name));
}
public PropertySource<?> get(String name) {
int index = this.propertySourceList.indexOf(PropertySource.named(name));
return index == -1 ? null : this.propertySourceList.get(index);
}
public Iterator<PropertySource<?>> iterator() {
return this.propertySourceList.iterator();
}
/**
* Add the given property source object with highest precedence.
*/
public void addFirst(PropertySource<?> propertySource) {
// if (logger.isDebugEnabled()) {
// logger.debug(String.format("Adding [%s] PropertySource with highest search precedence",
// propertySource.getName()));
// }
removeIfPresent(propertySource);
this.propertySourceList.addFirst(propertySource);
}
/**
* Add the given property source object with lowest precedence.
*/
public void addLast(PropertySource<?> propertySource) {
// if (logger.isDebugEnabled()) {
// logger.debug(String.format("Adding [%s] PropertySource with lowest search precedence",
// propertySource.getName()));
// }
removeIfPresent(propertySource);
this.propertySourceList.addLast(propertySource);
}
/**
* Add the given property source object with precedence immediately higher than
* the named relative property source.
*/
public void addBefore(String relativePropertySourceName, PropertySource<?> propertySource) {
// if (logger.isDebugEnabled()) {
// logger.debug(String.format("Adding [%s] PropertySource with search precedence immediately higher than [%s]",
// propertySource.getName(), relativePropertySourceName));
// }
assertLegalRelativeAddition(relativePropertySourceName, propertySource);
removeIfPresent(propertySource);
int index = assertPresentAndGetIndex(relativePropertySourceName);
addAtIndex(index, propertySource);
}
/**
* Add the given property source object with precedence immediately lower than
* the named relative property source.
*/
public void addAfter(String relativePropertySourceName, PropertySource<?> propertySource) {
// if (logger.isDebugEnabled()) {
// logger.debug(String.format("Adding [%s] PropertySource with search precedence immediately lower than [%s]",
// propertySource.getName(), relativePropertySourceName));
// }
assertLegalRelativeAddition(relativePropertySourceName, propertySource);
removeIfPresent(propertySource);
int index = assertPresentAndGetIndex(relativePropertySourceName);
addAtIndex(index + 1, propertySource);
}
/**
* Return the precedence of the given property source, {@code -1} if not found.
*/
public int precedenceOf(PropertySource<?> propertySource) {
return this.propertySourceList.indexOf(propertySource);
}
/**
* Remove and return the property source with the given name, {@code null} if
* not found.
*
* @param name the name of the property source to find and remove
*/
public PropertySource<?> remove(String name) {
// if (logger.isDebugEnabled()) {
// logger.debug(String.format("Removing [%s] PropertySource", name));
// }
int index = this.propertySourceList.indexOf(PropertySource.named(name));
return index == -1 ? null : this.propertySourceList.remove(index);
}
/**
* Replace the property source with the given name with the given property
* source object.
*
* @param name the name of the property source to find and replace
* @param propertySource the replacement property source
* @throws IllegalArgumentException if no property source with the given name is
* present
* @see #contains
*/
public void replace(String name, PropertySource<?> propertySource) {
// if (logger.isDebugEnabled()) {
// logger.debug(String.format("Replacing [%s] PropertySource with [%s]",
// name, propertySource.getName()));
// }
int index = assertPresentAndGetIndex(name);
this.propertySourceList.set(index, propertySource);
}
/**
* Return the number of {@link PropertySource} objects contained.
*/
public int size() {
return this.propertySourceList.size();
}
@Override
public String toString() {
String[] names = new String[this.size()];
for (int i = 0; i < size(); i++) {
names[i] = this.propertySourceList.get(i).getName();
}
return String.format("[%s]", arrayToCommaDelimitedString(names));
}
/**
* Ensure that the given property source is not being added relative to itself.
*/
protected void assertLegalRelativeAddition(String relativePropertySourceName, PropertySource<?> propertySource) {
// String newPropertySourceName = propertySource.getName();
// Assert.isTrue(!relativePropertySourceName.equals(newPropertySourceName),
// String.format(ILLEGAL_RELATIVE_ADDITION_MESSAGE, newPropertySourceName));
}
/**
* Remove the given property source if it is present.
*/
protected void removeIfPresent(PropertySource<?> propertySource) {
if (this.propertySourceList.contains(propertySource)) {
this.propertySourceList.remove(propertySource);
}
}
/**
* Add the given property source at a particular index in the list.
*/
private void addAtIndex(int index, PropertySource<?> propertySource) {
removeIfPresent(propertySource);
this.propertySourceList.add(index, propertySource);
}
/**
* Assert that the named property source is present and return its index.
*
* @param name the {@linkplain PropertySource#getName() name of the property
* source} to find
* @throws IllegalArgumentException if the named property source is not present
*/
private int assertPresentAndGetIndex(String name) {
int index = this.propertySourceList.indexOf(PropertySource.named(name));
// Assert.isTrue(index >= 0, String.format(NON_EXISTENT_PROPERTY_SOURCE_MESSAGE, name));
return index;
}
/**
* Convenience method to return a String array as a delimited (e.g. CSV) String.
* E.g. useful for {@code toString()} implementations.
*
* @param arr the array to display
* @param delim the delimiter to use (probably a ",")
* @return the delimited String
*/
private static String arrayToDelimitedString(Object[] arr, String delim) {
if (arr == null || arr.length == 0) {
return "";
}
if (arr.length == 1) {
return nullSafeToString(arr[0]);
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < arr.length; i++) {
if (i > 0) {
sb.append(delim);
}
sb.append(arr[i]);
}
return sb.toString();
}
/**
* Return a String representation of the specified Object.
* <p>
* Builds a String representation of the contents in case of an array. Returns
* {@code "null"} if {@code obj} is {@code null}.
*
* @param obj the object to build a String representation for
* @return a String representation of {@code obj}
*/
private static String nullSafeToString(Object obj) {
if (obj == null) {
return "null";
}
if (obj instanceof String) {
return (String) obj;
}
if (obj instanceof Object[]) {
return nullSafeToString((Object[]) obj);
}
if (obj instanceof boolean[]) {
return nullSafeToString((boolean[]) obj);
}
if (obj instanceof byte[]) {
return nullSafeToString((byte[]) obj);
}
if (obj instanceof char[]) {
return nullSafeToString((char[]) obj);
}
if (obj instanceof double[]) {
return nullSafeToString((double[]) obj);
}
if (obj instanceof float[]) {
return nullSafeToString((float[]) obj);
}
if (obj instanceof int[]) {
return nullSafeToString((int[]) obj);
}
if (obj instanceof long[]) {
return nullSafeToString((long[]) obj);
}
if (obj instanceof short[]) {
return nullSafeToString((short[]) obj);
}
String str = obj.toString();
return (str != null ? str : "");
}
/**
* Convenience method to return a String array as a CSV String. E.g. useful for
* {@code toString()} implementations.
*
* @param arr the array to display
* @return the delimited String
*/
private static String arrayToCommaDelimitedString(Object[] arr) {
return arrayToDelimitedString(arr, ",");
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 2002-2019 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 com.taobao.arthas.core.env;
import java.util.Map;
import java.util.Properties;
/**
* {@link PropertySource} implementation that extracts properties from a
* {@link java.util.Properties} object.
*
* <p>
* Note that because a {@code Properties} object is technically an
* {@code <Object, Object>} {@link java.util.Hashtable Hashtable}, one may
* contain non-{@code String} keys or values. This implementation, however is
* restricted to accessing only {@code String}-based keys and values, in the
* same fashion as {@link Properties#getProperty} and
* {@link Properties#setProperty}.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1
*/
public class PropertiesPropertySource extends MapPropertySource {
@SuppressWarnings({ "rawtypes", "unchecked" })
public PropertiesPropertySource(String name, Properties source) {
super(name, (Map) source);
}
protected PropertiesPropertySource(String name, Map<String, Object> source) {
super(name, source);
}
@Override
public String[] getPropertyNames() {
synchronized (this.source) {
return super.getPropertyNames();
}
}
}

View File

@ -0,0 +1,244 @@
/*
* Copyright 2002-2019 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 com.taobao.arthas.core.env;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* Utility class for working with Strings that have placeholder values in them.
* A placeholder takes the form {@code ${name}}. Using
* {@code PropertyPlaceholderHelper} these placeholders can be substituted for
* user-supplied values.
* <p>
* Values for substitution can be supplied using a {@link Properties} instance
* or using a {@link PlaceholderResolver}.
*
* @author Juergen Hoeller
* @author Rob Harrop
* @since 3.0
*/
public class PropertyPlaceholderHelper {
private static final Map<String, String> wellKnownSimplePrefixes = new HashMap<String, String>(4);
static {
wellKnownSimplePrefixes.put("}", "{");
wellKnownSimplePrefixes.put("]", "[");
wellKnownSimplePrefixes.put(")", "(");
}
private final String placeholderPrefix;
private final String placeholderSuffix;
private final String simplePrefix;
private final String valueSeparator;
private final boolean ignoreUnresolvablePlaceholders;
/**
* Creates a new {@code PropertyPlaceholderHelper} that uses the supplied prefix
* and suffix. Unresolvable placeholders are ignored.
*
* @param placeholderPrefix the prefix that denotes the start of a placeholder
* @param placeholderSuffix the suffix that denotes the end of a placeholder
*/
public PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix) {
this(placeholderPrefix, placeholderSuffix, null, true);
}
/**
* Creates a new {@code PropertyPlaceholderHelper} that uses the supplied prefix
* and suffix.
*
* @param placeholderPrefix the prefix that denotes the start of a
* placeholder
* @param placeholderSuffix the suffix that denotes the end of a
* placeholder
* @param valueSeparator the separating character between the
* placeholder variable and the associated
* default value, if any
* @param ignoreUnresolvablePlaceholders indicates whether unresolvable
* placeholders should be ignored
* ({@code true}) or cause an exception
* ({@code false})
*/
public PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix, String valueSeparator,
boolean ignoreUnresolvablePlaceholders) {
this.placeholderPrefix = placeholderPrefix;
this.placeholderSuffix = placeholderSuffix;
String simplePrefixForSuffix = wellKnownSimplePrefixes.get(this.placeholderSuffix);
if (simplePrefixForSuffix != null && this.placeholderPrefix.endsWith(simplePrefixForSuffix)) {
this.simplePrefix = simplePrefixForSuffix;
} else {
this.simplePrefix = this.placeholderPrefix;
}
this.valueSeparator = valueSeparator;
this.ignoreUnresolvablePlaceholders = ignoreUnresolvablePlaceholders;
}
/**
* Replaces all placeholders of format {@code ${name}} with the corresponding
* property from the supplied {@link Properties}.
*
* @param value the value containing the placeholders to be replaced
* @param properties the {@code Properties} to use for replacement
* @return the supplied value with placeholders replaced inline
*/
public String replacePlaceholders(String value, final Properties properties) {
return replacePlaceholders(value, new PlaceholderResolver() {
public String resolvePlaceholder(String placeholderName) {
return properties.getProperty(placeholderName);
}
});
}
/**
* Replaces all placeholders of format {@code ${name}} with the value returned
* from the supplied {@link PlaceholderResolver}.
*
* @param value the value containing the placeholders to be
* replaced
* @param placeholderResolver the {@code PlaceholderResolver} to use for
* replacement
* @return the supplied value with placeholders replaced inline
*/
public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
return parseStringValue(value, placeholderResolver, null);
}
protected String parseStringValue(String value, PlaceholderResolver placeholderResolver,
Set<String> visitedPlaceholders) {
int startIndex = value.indexOf(this.placeholderPrefix);
if (startIndex == -1) {
return value;
}
StringBuilder result = new StringBuilder(value);
while (startIndex != -1) {
int endIndex = findPlaceholderEndIndex(result, startIndex);
if (endIndex != -1) {
String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
String originalPlaceholder = placeholder;
if (visitedPlaceholders == null) {
visitedPlaceholders = new HashSet<String>(4);
}
if (!visitedPlaceholders.add(originalPlaceholder)) {
throw new IllegalArgumentException(
"Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
}
// Recursive invocation, parsing placeholders contained in the placeholder key.
placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
// Now obtain the value for the fully resolved key...
String propVal = placeholderResolver.resolvePlaceholder(placeholder);
if (propVal == null && this.valueSeparator != null) {
int separatorIndex = placeholder.indexOf(this.valueSeparator);
if (separatorIndex != -1) {
String actualPlaceholder = placeholder.substring(0, separatorIndex);
String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
if (propVal == null) {
propVal = defaultValue;
}
}
}
if (propVal != null) {
// Recursive invocation, parsing placeholders contained in the
// previously resolved placeholder value.
propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
} else if (this.ignoreUnresolvablePlaceholders) {
// Proceed with unprocessed value.
startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
} else {
throw new IllegalArgumentException(
"Could not resolve placeholder '" + placeholder + "'" + " in value \"" + value + "\"");
}
visitedPlaceholders.remove(originalPlaceholder);
} else {
startIndex = -1;
}
}
return result.toString();
}
private int findPlaceholderEndIndex(CharSequence buf, int startIndex) {
int index = startIndex + this.placeholderPrefix.length();
int withinNestedPlaceholder = 0;
while (index < buf.length()) {
if (substringMatch(buf, index, this.placeholderSuffix)) {
if (withinNestedPlaceholder > 0) {
withinNestedPlaceholder--;
index = index + this.placeholderSuffix.length();
} else {
return index;
}
} else if (substringMatch(buf, index, this.simplePrefix)) {
withinNestedPlaceholder++;
index = index + this.simplePrefix.length();
} else {
index++;
}
}
return -1;
}
/**
* Test whether the given string matches the given substring at the given index.
*
* @param str the original string (or StringBuilder)
* @param index the index in the original string to start matching against
* @param substring the substring to match at the given index
*/
public static boolean substringMatch(CharSequence str, int index, CharSequence substring) {
if (index + substring.length() > str.length()) {
return false;
}
for (int i = 0; i < substring.length(); i++) {
if (str.charAt(index + i) != substring.charAt(i)) {
return false;
}
}
return true;
}
/**
* Strategy interface used to resolve replacement values for placeholders
* contained in Strings.
*/
@FunctionalInterface
public interface PlaceholderResolver {
/**
* Resolve the supplied placeholder name to the replacement value.
*
* @param placeholderName the name of the placeholder to resolve
* @return the replacement value, or {@code null} if no replacement is to be
* made
*/
String resolvePlaceholder(String placeholderName);
}
}

View File

@ -0,0 +1,123 @@
/*
* Copyright 2002-2016 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 com.taobao.arthas.core.env;
/**
* Interface for resolving properties against any underlying source.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1
* @see Environment
* @see PropertySourcesPropertyResolver
*/
public interface PropertyResolver {
/**
* Return whether the given property key is available for resolution, i.e. if
* the value for the given key is not {@code null}.
*/
boolean containsProperty(String key);
/**
* Return the property value associated with the given key, or {@code null} if
* the key cannot be resolved.
*
* @param key the property name to resolve
* @see #getProperty(String, String)
* @see #getProperty(String, Class)
* @see #getRequiredProperty(String)
*/
String getProperty(String key);
/**
* Return the property value associated with the given key, or
* {@code defaultValue} if the key cannot be resolved.
*
* @param key the property name to resolve
* @param defaultValue the default value to return if no value is found
* @see #getRequiredProperty(String)
* @see #getProperty(String, Class)
*/
String getProperty(String key, String defaultValue);
/**
* Return the property value associated with the given key, or {@code null} if
* the key cannot be resolved.
*
* @param key the property name to resolve
* @param targetType the expected type of the property value
* @see #getRequiredProperty(String, Class)
*/
<T> T getProperty(String key, Class<T> targetType);
/**
* Return the property value associated with the given key, or
* {@code defaultValue} if the key cannot be resolved.
*
* @param key the property name to resolve
* @param targetType the expected type of the property value
* @param defaultValue the default value to return if no value is found
* @see #getRequiredProperty(String, Class)
*/
<T> T getProperty(String key, Class<T> targetType, T defaultValue);
/**
* Return the property value associated with the given key (never {@code null}).
*
* @throws IllegalStateException if the key cannot be resolved
* @see #getRequiredProperty(String, Class)
*/
String getRequiredProperty(String key) throws IllegalStateException;
/**
* Return the property value associated with the given key, converted to the
* given targetType (never {@code null}).
*
* @throws IllegalStateException if the given key cannot be resolved
*/
<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;
/**
* Resolve ${...} placeholders in the given text, replacing them with
* corresponding property values as resolved by {@link #getProperty}.
* Unresolvable placeholders with no default value are ignored and passed
* through unchanged.
*
* @param text the String to resolve
* @return the resolved String (never {@code null})
* @throws IllegalArgumentException if given text is {@code null}
* @see #resolveRequiredPlaceholders
* @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders(String)
*/
String resolvePlaceholders(String text);
/**
* Resolve ${...} placeholders in the given text, replacing them with
* corresponding property values as resolved by {@link #getProperty}.
* Unresolvable placeholders with no default value will cause an
* IllegalArgumentException to be thrown.
*
* @return the resolved String (never {@code null})
* @throws IllegalArgumentException if given text is {@code null} or if any
* placeholders are unresolvable
* @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders(String,
* boolean)
*/
String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;
}

View File

@ -0,0 +1,327 @@
/*
* Copyright 2002-2018 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 com.taobao.arthas.core.env;
import java.util.Arrays;
import com.sun.tools.javac.util.Log;
/**
* Abstract base class representing a source of name/value property pairs. The
* underlying {@linkplain #getSource() source object} may be of any type
* {@code T} that encapsulates properties. Examples include
* {@link java.util.Properties} objects, {@link java.util.Map} objects,
* {@code ServletContext} and {@code ServletConfig} objects (for access to init
* parameters). Explore the {@code PropertySource} type hierarchy to see
* provided implementations.
*
* <p>
* {@code PropertySource} objects are not typically used in isolation, but
* rather through a {@link PropertySources} object, which aggregates property
* sources and in conjunction with a {@link PropertyResolver} implementation
* that can perform precedence-based searches across the set of
* {@code PropertySources}.
*
* <p>
* {@code PropertySource} identity is determined not based on the content of
* encapsulated properties, but rather based on the {@link #getName() name} of
* the {@code PropertySource} alone. This is useful for manipulating
* {@code PropertySource} objects when in collection contexts. See operations in
* {@link MutablePropertySources} as well as the {@link #named(String)} and
* {@link #toString()} methods for details.
*
* <p>
* Note that when working
* with @{@link org.springframework.context.annotation.Configuration
* Configuration} classes that
* the @{@link org.springframework.context.annotation.PropertySource
* PropertySource} annotation provides a convenient and declarative way of
* adding property sources to the enclosing {@code Environment}.
*
* @author Chris Beams
* @since 3.1
* @param <T> the source type
* @see PropertySources
* @see PropertyResolver
* @see PropertySourcesPropertyResolver
* @see MutablePropertySources
* @see org.springframework.context.annotation.PropertySource
*/
public abstract class PropertySource<T> {
protected final String name;
protected final T source;
/**
* Create a new {@code PropertySource} with the given name and source object.
*/
public PropertySource(String name, T source) {
this.name = name;
this.source = source;
}
/**
* Create a new {@code PropertySource} with the given name and with a new
* {@code Object} instance as the underlying source.
* <p>
* Often useful in testing scenarios when creating anonymous implementations
* that never query an actual source but rather return hard-coded values.
*/
@SuppressWarnings("unchecked")
public PropertySource(String name) {
this(name, (T) new Object());
}
/**
* Return the name of this {@code PropertySource}.
*/
public String getName() {
return this.name;
}
/**
* Return the underlying source object for this {@code PropertySource}.
*/
public T getSource() {
return this.source;
}
/**
* Return whether this {@code PropertySource} contains the given name.
* <p>
* This implementation simply checks for a {@code null} return value from
* {@link #getProperty(String)}. Subclasses may wish to implement a more
* efficient algorithm if possible.
*
* @param name the property name to find
*/
public boolean containsProperty(String name) {
return (getProperty(name) != null);
}
/**
* Return the value associated with the given name, or {@code null} if not
* found.
*
* @param name the property to find
* @see PropertyResolver#getRequiredProperty(String)
*/
public abstract Object getProperty(String name);
/**
* This {@code PropertySource} object is equal to the given object if:
* <ul>
* <li>they are the same instance
* <li>the {@code name} properties for both objects are equal
* </ul>
* <p>
* No properties other than {@code name} are evaluated.
*/
@Override
public boolean equals(Object other) {
return (this == other
|| (other instanceof PropertySource && nullSafeEquals(this.name, ((PropertySource<?>) other).name)));
}
/**
* Return a hash code derived from the {@code name} property of this
* {@code PropertySource} object.
*/
@Override
public int hashCode() {
return this.name.hashCode();
}
/**
* Produce concise output (type and name) if the current log level does not
* include debug. If debug is enabled, produce verbose output including the hash
* code of the PropertySource instance and every name/value property pair.
* <p>
* This variable verbosity is useful as a property source such as system
* properties or environment variables may contain an arbitrary number of
* property pairs, potentially leading to difficult to read exception and log
* messages.
*
* @see Log#isDebugEnabled()
*/
@Override
public String toString() {
return getClass().getSimpleName() + " {name='" + this.name + "'}";
}
/**
* Return a {@code PropertySource} implementation intended for collection
* comparison purposes only.
* <p>
* Primarily for internal use, but given a collection of {@code PropertySource}
* objects, may be used as follows:
*
* <pre class="code">
* {
* &#64;code
* List<PropertySource<?>> sources = new ArrayList<PropertySource<?>>();
* sources.add(new MapPropertySource("sourceA", mapA));
* sources.add(new MapPropertySource("sourceB", mapB));
* assert sources.contains(PropertySource.named("sourceA"));
* assert sources.contains(PropertySource.named("sourceB"));
* assert !sources.contains(PropertySource.named("sourceC"));
* }
* </pre>
*
* The returned {@code PropertySource} will throw
* {@code UnsupportedOperationException} if any methods other than
* {@code equals(Object)}, {@code hashCode()}, and {@code toString()} are
* called.
*
* @param name the name of the comparison {@code PropertySource} to be created
* and returned.
*/
public static PropertySource<?> named(String name) {
return new ComparisonPropertySource(name);
}
/**
* Determine if the given objects are equal, returning {@code true} if both are
* {@code null} or {@code false} if only one is {@code null}.
* <p>
* Compares arrays with {@code Arrays.equals}, performing an equality check
* based on the array elements rather than the array reference.
*
* @param o1 first Object to compare
* @param o2 second Object to compare
* @return whether the given objects are equal
* @see Object#equals(Object)
* @see java.util.Arrays#equals
*/
public static boolean nullSafeEquals(Object o1, Object o2) {
if (o1 == o2) {
return true;
}
if (o1 == null || o2 == null) {
return false;
}
if (o1.equals(o2)) {
return true;
}
if (o1.getClass().isArray() && o2.getClass().isArray()) {
return arrayEquals(o1, o2);
}
return false;
}
/**
* Compare the given arrays with {@code Arrays.equals}, performing an equality
* check based on the array elements rather than the array reference.
*
* @param o1 first array to compare
* @param o2 second array to compare
* @return whether the given objects are equal
* @see #nullSafeEquals(Object, Object)
* @see java.util.Arrays#equals
*/
private static boolean arrayEquals(Object o1, Object o2) {
if (o1 instanceof Object[] && o2 instanceof Object[]) {
return Arrays.equals((Object[]) o1, (Object[]) o2);
}
if (o1 instanceof boolean[] && o2 instanceof boolean[]) {
return Arrays.equals((boolean[]) o1, (boolean[]) o2);
}
if (o1 instanceof byte[] && o2 instanceof byte[]) {
return Arrays.equals((byte[]) o1, (byte[]) o2);
}
if (o1 instanceof char[] && o2 instanceof char[]) {
return Arrays.equals((char[]) o1, (char[]) o2);
}
if (o1 instanceof double[] && o2 instanceof double[]) {
return Arrays.equals((double[]) o1, (double[]) o2);
}
if (o1 instanceof float[] && o2 instanceof float[]) {
return Arrays.equals((float[]) o1, (float[]) o2);
}
if (o1 instanceof int[] && o2 instanceof int[]) {
return Arrays.equals((int[]) o1, (int[]) o2);
}
if (o1 instanceof long[] && o2 instanceof long[]) {
return Arrays.equals((long[]) o1, (long[]) o2);
}
if (o1 instanceof short[] && o2 instanceof short[]) {
return Arrays.equals((short[]) o1, (short[]) o2);
}
return false;
}
/**
* {@code PropertySource} to be used as a placeholder in cases where an actual
* property source cannot be eagerly initialized at application context creation
* time. For example, a {@code ServletContext}-based property source must wait
* until the {@code ServletContext} object is available to its enclosing
* {@code ApplicationContext}. In such cases, a stub should be used to hold the
* intended default position/order of the property source, then be replaced
* during context refresh.
*
* @see org.springframework.context.support.AbstractApplicationContext#initPropertySources()
* @see org.springframework.web.context.support.StandardServletEnvironment
* @see org.springframework.web.context.support.ServletContextPropertySource
*/
public static class StubPropertySource extends PropertySource<Object> {
public StubPropertySource(String name) {
super(name, new Object());
}
/**
* Always returns {@code null}.
*/
@Override
public String getProperty(String name) {
return null;
}
}
/**
* @see PropertySource#named(String)
*/
static class ComparisonPropertySource extends StubPropertySource {
private static final String USAGE_ERROR = "ComparisonPropertySource instances are for use with collection comparison only";
public ComparisonPropertySource(String name) {
super(name);
}
@Override
public Object getSource() {
throw new UnsupportedOperationException(USAGE_ERROR);
}
@Override
public boolean containsProperty(String name) {
throw new UnsupportedOperationException(USAGE_ERROR);
}
@Override
public String getProperty(String name) {
throw new UnsupportedOperationException(USAGE_ERROR);
}
@Override
public String toString() {
return String.format("%s [name='%s']", getClass().getSimpleName(), this.name);
}
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2002-2018 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 com.taobao.arthas.core.env;
/**
* Holder containing one or more {@link PropertySource} objects.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1
* @see PropertySource
*/
public interface PropertySources extends Iterable<PropertySource<?>> {
/**
* Return whether a property source with the given name is contained.
*
* @param name the {@linkplain PropertySource#getName() name of the property
* source} to find
*/
boolean contains(String name);
/**
* Return the property source with the given name, {@code null} if not found.
*
* @param name the {@linkplain PropertySource#getName() name of the property
* source} to find
*/
PropertySource<?> get(String name);
}

View File

@ -0,0 +1,129 @@
/*
* Copyright 2002-2018 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 com.taobao.arthas.core.env;
/**
* {@link PropertyResolver} implementation that resolves property values against
* an underlying set of {@link PropertySources}.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1
* @see PropertySource
* @see PropertySources
* @see AbstractEnvironment
*/
public class PropertySourcesPropertyResolver extends AbstractPropertyResolver {
private final PropertySources propertySources;
/**
* Create a new resolver against the given property sources.
*
* @param propertySources the set of {@link PropertySource} objects to use
*/
public PropertySourcesPropertyResolver(PropertySources propertySources) {
this.propertySources = propertySources;
}
@Override
public boolean containsProperty(String key) {
if (this.propertySources != null) {
for (PropertySource<?> propertySource : this.propertySources) {
if (propertySource.containsProperty(key)) {
return true;
}
}
}
return false;
}
@Override
public String getProperty(String key) {
return getProperty(key, String.class, true);
}
@Override
public <T> T getProperty(String key, Class<T> targetValueType) {
return getProperty(key, targetValueType, true);
}
@Override
protected String getPropertyAsRawString(String key) {
return getProperty(key, String.class, false);
}
// protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
// if (this.propertySources != null) {
// for (PropertySource<?> propertySource : this.propertySources) {
// Object value = propertySource.getProperty(key);
// if (value != null) {
// if (resolveNestedPlaceholders && value instanceof String) {
// value = resolveNestedPlaceholders((String) value);
// }
// logKeyFound(key, propertySource, value);
// return convertValueIfNecessary(value, targetValueType);
// }
// }
// }
// return null;
// }
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
if (this.propertySources != null) {
for (PropertySource<?> propertySource : this.propertySources) {
Object value;
if ((value = propertySource.getProperty(key)) != null) {
Class<?> valueType = value.getClass();
if (resolveNestedPlaceholders && value instanceof String) {
value = resolveNestedPlaceholders((String) value);
}
if (!this.conversionService.canConvert(valueType, targetValueType)) {
throw new IllegalArgumentException(
String.format("Cannot convert value [%s] from source type [%s] to target type [%s]",
value, valueType.getSimpleName(), targetValueType.getSimpleName()));
}
return this.conversionService.convert(value, targetValueType);
}
}
}
return null;
}
/**
* Log the given key as found in the given {@link PropertySource}, resulting in
* the given value.
* <p>
* The default implementation writes a debug log message with key and source. As
* of 4.3.3, this does not log the value anymore in order to avoid accidental
* logging of sensitive settings. Subclasses may override this method to change
* the log level and/or log message, including the property's value if desired.
*
* @param key the key found
* @param propertySource the {@code PropertySource} that the key has been found
* in
* @param value the corresponding value
* @since 4.3.1
*/
protected void logKeyFound(String key, PropertySource<?> propertySource, Object value) {
// if (logger.isDebugEnabled()) {
// logger.debug("Found key '" + key + "' in PropertySource '" + propertySource.getName() +
// "' with value of type " + value.getClass().getSimpleName());
// }
}
}

View File

@ -0,0 +1,123 @@
/*
* Copyright 2002-2018 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 com.taobao.arthas.core.env;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
/**
* Read-only {@code Map<String, String>} implementation that is backed by system
* properties or environment variables.
*
* <p>
* Used by {@link AbstractApplicationContext} when a {@link SecurityManager}
* prohibits access to {@link System#getProperties()} or
* {@link System#getenv()}. It is for this reason that the implementations of
* {@link #keySet()}, {@link #entrySet()}, and {@link #values()} always return
* empty even though {@link #get(Object)} may in fact return non-null if the
* current security manager allows access to individual keys.
*
* @author Arjen Poutsma
* @author Chris Beams
* @since 3.0
*/
abstract class ReadOnlySystemAttributesMap implements Map<String, String> {
@Override
public boolean containsKey(Object key) {
return (get(key) != null);
}
/**
* Returns the value to which the specified key is mapped, or {@code null} if
* this map contains no mapping for the key.
*
* @param key the name of the system attribute to retrieve
* @throws IllegalArgumentException if given key is non-String
*/
@Override
public String get(Object key) {
if (!(key instanceof String)) {
throw new IllegalArgumentException(
"Type of key [" + key.getClass().getName() + "] must be java.lang.String");
}
return getSystemAttribute((String) key);
}
@Override
public boolean isEmpty() {
return false;
}
/**
* Template method that returns the underlying system attribute.
* <p>
* Implementations typically call {@link System#getProperty(String)} or
* {@link System#getenv(String)} here.
*/
protected abstract String getSystemAttribute(String attributeName);
// Unsupported
@Override
public int size() {
throw new UnsupportedOperationException();
}
@Override
public String put(String key, String value) {
throw new UnsupportedOperationException();
}
@Override
public boolean containsValue(Object value) {
throw new UnsupportedOperationException();
}
@Override
public String remove(Object key) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public Set<String> keySet() {
return Collections.emptySet();
}
@Override
public void putAll(Map<? extends String, ? extends String> map) {
throw new UnsupportedOperationException();
}
@Override
public Collection<String> values() {
return Collections.emptySet();
}
@Override
public Set<Entry<String, String>> entrySet() {
return Collections.emptySet();
}
}

View File

@ -0,0 +1,157 @@
/*
* Copyright 2002-2015 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 com.taobao.arthas.core.env;
import java.util.Map;
/**
* Specialization of {@link MapPropertySource} designed for use with
* {@linkplain AbstractEnvironment#getSystemEnvironment() system environment
* variables}. Compensates for constraints in Bash and other shells that do not
* allow for variables containing the period character and/or hyphen character;
* also allows for uppercase variations on property names for more idiomatic
* shell use.
*
* <p>
* For example, a call to {@code getProperty("foo.bar")} will attempt to find a
* value for the original property or any 'equivalent' property, returning the
* first found:
* <ul>
* <li>{@code foo.bar} - the original name</li>
* <li>{@code foo_bar} - with underscores for periods (if any)</li>
* <li>{@code FOO.BAR} - original, with upper case</li>
* <li>{@code FOO_BAR} - with underscores and upper case</li>
* </ul>
* Any hyphen variant of the above would work as well, or even mix dot/hyphen
* variants.
*
* <p>
* The same applies for calls to {@link #containsProperty(String)}, which
* returns {@code true} if any of the above properties are present, otherwise
* {@code false}.
*
* <p>
* This feature is particularly useful when specifying active or default
* profiles as environment variables. The following is not allowable under Bash:
*
* <pre class="code">
* spring.profiles.active=p1 java -classpath ... MyApp
* </pre>
*
* However, the following syntax is permitted and is also more conventional:
*
* <pre class="code">
* SPRING_PROFILES_ACTIVE=p1 java -classpath ... MyApp
* </pre>
*
* <p>
* Enable debug- or trace-level logging for this class (or package) for messages
* explaining when these 'property name resolutions' occur.
*
* <p>
* This property source is included by default in {@link StandardEnvironment}
* and all its subclasses.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1
* @see StandardEnvironment
* @see AbstractEnvironment#getSystemEnvironment()
* @see AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME
*/
public class SystemEnvironmentPropertySource extends MapPropertySource {
/**
* Create a new {@code SystemEnvironmentPropertySource} with the given name and
* delegating to the given {@code MapPropertySource}.
*/
public SystemEnvironmentPropertySource(String name, Map<String, Object> source) {
super(name, source);
}
/**
* Return {@code true} if a property with the given name or any
* underscore/uppercase variant thereof exists in this property source.
*/
@Override
public boolean containsProperty(String name) {
return (getProperty(name) != null);
}
/**
* This implementation returns {@code true} if a property with the given name or
* any underscore/uppercase variant thereof exists in this property source.
*/
@Override
public Object getProperty(String name) {
String actualName = resolvePropertyName(name);
return super.getProperty(actualName);
}
/**
* Check to see if this property source contains a property with the given name,
* or any underscore / uppercase variation thereof. Return the resolved name if
* one is found or otherwise the original name. Never returns {@code null}.
*/
protected final String resolvePropertyName(String name) {
String resolvedName = checkPropertyName(name);
if (resolvedName != null) {
return resolvedName;
}
String uppercasedName = name.toUpperCase();
if (!name.equals(uppercasedName)) {
resolvedName = checkPropertyName(uppercasedName);
if (resolvedName != null) {
return resolvedName;
}
}
return name;
}
private String checkPropertyName(String name) {
// Check name as-is
if (containsKey(name)) {
return name;
}
// Check name with just dots replaced
String noDotName = name.replace('.', '_');
if (!name.equals(noDotName) && containsKey(noDotName)) {
return noDotName;
}
// Check name with just hyphens replaced
String noHyphenName = name.replace('-', '_');
if (!name.equals(noHyphenName) && containsKey(noHyphenName)) {
return noHyphenName;
}
// Check name with dots and hyphens replaced
String noDotNoHyphenName = noDotName.replace('-', '_');
if (!noDotName.equals(noDotNoHyphenName) && containsKey(noDotNoHyphenName)) {
return noDotNoHyphenName;
}
// Give up
return null;
}
private boolean containsKey(String name) {
return (isSecurityManagerPresent() ? this.source.keySet().contains(name) : this.source.containsKey(name));
}
protected boolean isSecurityManagerPresent() {
return (System.getSecurityManager() != null);
}
}

View File

@ -0,0 +1,116 @@
/*
* Copyright 2002-2018 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 com.taobao.arthas.core.env;
/**
* Helper class for resolving placeholders in texts. Usually applied to file
* paths.
*
* <p>
* A text may contain {@code ${...}} placeholders, to be resolved as system
* properties: e.g. {@code ${user.dir}}. Default values can be supplied using
* the ":" separator between key and value.
*
* @author Juergen Hoeller
* @author Rob Harrop
* @author Dave Syer
* @since 1.2.5
* @see #PLACEHOLDER_PREFIX
* @see #PLACEHOLDER_SUFFIX
* @see System#getProperty(String)
*/
public abstract class SystemPropertyUtils {
/** Prefix for system property placeholders: "${". */
public static final String PLACEHOLDER_PREFIX = "${";
/** Suffix for system property placeholders: "}". */
public static final String PLACEHOLDER_SUFFIX = "}";
/** Value separator for system property placeholders: ":". */
public static final String VALUE_SEPARATOR = ":";
private static final PropertyPlaceholderHelper strictHelper = new PropertyPlaceholderHelper(PLACEHOLDER_PREFIX,
PLACEHOLDER_SUFFIX, VALUE_SEPARATOR, false);
private static final PropertyPlaceholderHelper nonStrictHelper = new PropertyPlaceholderHelper(PLACEHOLDER_PREFIX,
PLACEHOLDER_SUFFIX, VALUE_SEPARATOR, true);
/**
* Resolve {@code ${...}} placeholders in the given text, replacing them with
* corresponding system property values.
*
* @param text the String to resolve
* @return the resolved String
* @throws IllegalArgumentException if there is an unresolvable placeholder
* @see #PLACEHOLDER_PREFIX
* @see #PLACEHOLDER_SUFFIX
*/
public static String resolvePlaceholders(String text) {
return resolvePlaceholders(text, false);
}
/**
* Resolve {@code ${...}} placeholders in the given text, replacing them with
* corresponding system property values. Unresolvable placeholders with no
* default value are ignored and passed through unchanged if the flag is set to
* {@code true}.
*
* @param text the String to resolve
* @param ignoreUnresolvablePlaceholders whether unresolved placeholders are to
* be ignored
* @return the resolved String
* @throws IllegalArgumentException if there is an unresolvable placeholder
* @see #PLACEHOLDER_PREFIX
* @see #PLACEHOLDER_SUFFIX and the "ignoreUnresolvablePlaceholders" flag is
* {@code false}
*/
public static String resolvePlaceholders(String text, boolean ignoreUnresolvablePlaceholders) {
PropertyPlaceholderHelper helper = (ignoreUnresolvablePlaceholders ? nonStrictHelper : strictHelper);
return helper.replacePlaceholders(text, new SystemPropertyPlaceholderResolver(text));
}
/**
* PlaceholderResolver implementation that resolves against system properties
* and system environment variables.
*/
private static class SystemPropertyPlaceholderResolver implements PropertyPlaceholderHelper.PlaceholderResolver {
private final String text;
public SystemPropertyPlaceholderResolver(String text) {
this.text = text;
}
@Override
public String resolvePlaceholder(String placeholderName) {
try {
String propVal = System.getProperty(placeholderName);
if (propVal == null) {
// Fall back to searching the system environment.
propVal = System.getenv(placeholderName);
}
return propVal;
} catch (Throwable ex) {
System.err.println("Could not resolve placeholder '" + placeholderName + "' in [" + this.text
+ "] as system property: " + ex);
return null;
}
}
}
}

View File

@ -0,0 +1,5 @@
/**
* from org.springframework.core.env
*/
package com.taobao.arthas.core.env;

View File

@ -0,0 +1,54 @@
package com.taobao.arthas.core.env;
import java.util.Properties;
import org.assertj.core.api.Assertions;
import org.junit.Test;
/**
*
* @author hengyunabc 2019-12-27
*
*/
public class ArthasEnvironmentTest {
@Test
public void test() {
ArthasEnvironment arthasEnvironment = new ArthasEnvironment();
Assertions.assertThat(arthasEnvironment.resolvePlaceholders("hello, ${java.version}"))
.isEqualTo("hello, " + System.getProperty("java.version"));
Assertions.assertThat(arthasEnvironment.resolvePlaceholders("hello, ${xxxxxxxxxxxxxxx}"))
.isEqualTo("hello, ${xxxxxxxxxxxxxxx}");
System.setProperty("xxxxxxxxxxxxxxx", "vvv");
Assertions.assertThat(arthasEnvironment.resolvePlaceholders("hello, ${xxxxxxxxxxxxxxx}"))
.isEqualTo("hello, vvv");
System.clearProperty("xxxxxxxxxxxxxxx");
}
@Test
public void test_properties() {
ArthasEnvironment arthasEnvironment = new ArthasEnvironment();
Properties properties1 = new Properties();
Properties properties2 = new Properties();
arthasEnvironment.addLast(new PropertiesPropertySource("test1", properties1));
arthasEnvironment.addLast(new PropertiesPropertySource("test2", properties2));
properties2.put("test.key", "2222");
Assertions.assertThat(arthasEnvironment.resolvePlaceholders("hello, ${test.key}")).isEqualTo("hello, 2222");
properties1.put("java.version", "test");
properties1.put("test.key", "test");
Assertions.assertThat(arthasEnvironment.resolvePlaceholders("hello, ${java.version}"))
.isEqualTo("hello, " + System.getProperty("java.version"));
Assertions.assertThat(arthasEnvironment.resolvePlaceholders("hello, ${test.key}")).isEqualTo("hello, test");
}
}

View File

@ -160,6 +160,13 @@
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<!-- use 2.9.1 for Java 7 projects -->
<version>3.11.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>