diff --git a/core/pom.xml b/core/pom.xml
index 9e25ad28f..5b0813aef 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -134,6 +134,7 @@
com.taobao.middlewarelogger.api
+
log4jlog4j
@@ -161,6 +162,12 @@
junittest
+
+ org.assertj
+ assertj-core
+ test
+
+
org.benfcfr
diff --git a/core/src/main/java/com/taobao/arthas/core/env/AbstractPropertyResolver.java b/core/src/main/java/com/taobao/arthas/core/env/AbstractPropertyResolver.java
new file mode 100644
index 000000000..9cc715aa8
--- /dev/null
+++ b/core/src/main/java/com/taobao/arthas/core/env/AbstractPropertyResolver.java
@@ -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 requiredProperties = new LinkedHashSet();
+
+ 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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 getProperty(String key, Class 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 getRequiredProperty(String key, Class 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.
+ *
+ * Invoked from {@link #getProperty} and its variants, implicitly resolving
+ * nested placeholders. In contrast, {@link #resolvePlaceholders} and
+ * {@link #resolveRequiredPlaceholders} do not 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);
+
+}
diff --git a/core/src/main/java/com/taobao/arthas/core/env/ArthasEnvironment.java b/core/src/main/java/com/taobao/arthas/core/env/ArthasEnvironment.java
new file mode 100644
index 000000000..d7bfccdf5
--- /dev/null
+++ b/core/src/main/java/com/taobao/arthas/core/env/ArthasEnvironment.java
@@ -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 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 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 getProperty(String key, Class targetType) {
+ return this.propertyResolver.getProperty(key, targetType);
+ }
+
+ @Override
+ public T getProperty(String key, Class 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 getRequiredProperty(String key, Class 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);
+ }
+
+}
diff --git a/core/src/main/java/com/taobao/arthas/core/env/ConfigurableConversionService.java b/core/src/main/java/com/taobao/arthas/core/env/ConfigurableConversionService.java
new file mode 100644
index 000000000..28ff54841
--- /dev/null
+++ b/core/src/main/java/com/taobao/arthas/core/env/ConfigurableConversionService.java
@@ -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 {
+
+}
diff --git a/core/src/main/java/com/taobao/arthas/core/env/ConfigurablePropertyResolver.java b/core/src/main/java/com/taobao/arthas/core/env/ConfigurablePropertyResolver.java
new file mode 100644
index 000000000..c0f63ae68
--- /dev/null
+++ b/core/src/main/java/com/taobao/arthas/core/env/ConfigurablePropertyResolver.java
@@ -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.
+ *
+ * The configurable nature of the returned conversion service allows for the
+ * convenient addition and removal of individual {@code Converter} instances:
+ *
+ *
+ *
+ * @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.
+ *
+ * Note: 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.
+ *
+ * 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;
+
+}
diff --git a/core/src/main/java/com/taobao/arthas/core/env/ConversionService.java b/core/src/main/java/com/taobao/arthas/core/env/ConversionService.java
new file mode 100644
index 000000000..4e26baf51
--- /dev/null
+++ b/core/src/main/java/com/taobao/arthas/core/env/ConversionService.java
@@ -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}.
+ *
+ * If this method returns {@code true}, it means {@link #convert(Object, Class)}
+ * is capable of converting an instance of {@code sourceType} to
+ * {@code targetType}.
+ *
+ * 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 convert(Object source, Class targetType);
+
+}
diff --git a/core/src/main/java/com/taobao/arthas/core/env/DefaultConversionService.java b/core/src/main/java/com/taobao/arthas/core/env/DefaultConversionService.java
new file mode 100644
index 000000000..462ce4bd3
--- /dev/null
+++ b/core/src/main/java/com/taobao/arthas/core/env/DefaultConversionService.java
@@ -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 convert(Object source, Class targetType) {
+ // TODO Auto-generated method stub
+ return (T) source;
+ }
+
+}
diff --git a/core/src/main/java/com/taobao/arthas/core/env/EnumerablePropertySource.java b/core/src/main/java/com/taobao/arthas/core/env/EnumerablePropertySource.java
new file mode 100644
index 000000000..189c7c9d2
--- /dev/null
+++ b/core/src/main/java/com/taobao/arthas/core/env/EnumerablePropertySource.java
@@ -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.
+ *
+ *
+ * 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 the source type
+ */
+public abstract class EnumerablePropertySource extends PropertySource {
+
+ 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.
+ *
+ * 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();
+
+}
diff --git a/core/src/main/java/com/taobao/arthas/core/env/Environment.java b/core/src/main/java/com/taobao/arthas/core/env/Environment.java
new file mode 100644
index 000000000..c6a8de970
--- /dev/null
+++ b/core/src/main/java/com/taobao/arthas/core/env/Environment.java
@@ -0,0 +1,5 @@
+package com.taobao.arthas.core.env;
+
+public interface Environment extends PropertyResolver {
+
+}
diff --git a/core/src/main/java/com/taobao/arthas/core/env/MapPropertySource.java b/core/src/main/java/com/taobao/arthas/core/env/MapPropertySource.java
new file mode 100644
index 000000000..825a11624
--- /dev/null
+++ b/core/src/main/java/com/taobao/arthas/core/env/MapPropertySource.java
@@ -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