From 4937b99d1259c05906e386079c248447049b5bc6 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 14 Feb 2020 02:40:08 +0800 Subject: [PATCH] support ArthasEnvironment. #986 --- core/pom.xml | 4 + .../arthas/core/config/BinderUtils.java | 110 +++++++++++++++++ .../com/taobao/arthas/core/config/Config.java | 19 +++ .../taobao/arthas/core/config/Configure.java | 8 +- .../arthas/core/config/FeatureCodec.java | 2 + .../arthas/core/config/NestedConfig.java | 17 +++ .../arthas/core/config/SecondConfig.java | 5 + .../taobao/arthas/core/config/TestConfig.java | 9 ++ .../core/env/AbstractPropertyResolver.java | 3 + .../arthas/core/env/ArthasEnvironment.java | 7 ++ .../env/ConfigurablePropertyResolver.java | 2 + .../ConfigurableConversionService.java | 6 +- .../arthas/core/env/convert/Converter.java | 40 ++++++ .../core/env/convert/ConvertiblePair.java | 52 ++++++++ .../env/convert/DefaultConversionService.java | 114 +++++++++++++++++- .../env/convert/ObjectToStringConverter.java | 31 +++++ .../env/convert/StringToArrayConverter.java | 33 +++++ .../env/convert/StringToBooleanConverter.java | 64 ++++++++++ .../env/convert/StringToEnumConverter.java | 12 ++ .../convert/StringToInetAddressConverter.java | 17 +++ .../env/convert/StringToIntegerConverter.java | 9 ++ .../env/convert/StringToLongConverter.java | 9 ++ .../arthas/core/server/ArthasBootstrap.java | 20 +++ .../arthas/core/config/ErrorProperties.java | 62 ++++++++++ .../core/config/PropertiesInjectUtilTest.java | 66 ++++++++++ .../com/taobao/arthas/core/config/Server.java | 59 +++++++++ .../arthas/core/config/ServerProperties.java | 81 +++++++++++++ .../core/config/ServerPropertiesTest.java | 76 ++++++++++++ .../com/taobao/arthas/core/config/Ssl.java | 72 +++++++++++ .../arthas/core/config/SystemObject.java | 34 ++++++ pom.xml | 5 + 31 files changed, 1037 insertions(+), 11 deletions(-) create mode 100644 core/src/main/java/com/taobao/arthas/core/config/BinderUtils.java create mode 100644 core/src/main/java/com/taobao/arthas/core/config/Config.java create mode 100644 core/src/main/java/com/taobao/arthas/core/config/NestedConfig.java create mode 100644 core/src/main/java/com/taobao/arthas/core/config/SecondConfig.java create mode 100644 core/src/main/java/com/taobao/arthas/core/config/TestConfig.java create mode 100644 core/src/main/java/com/taobao/arthas/core/env/convert/Converter.java create mode 100644 core/src/main/java/com/taobao/arthas/core/env/convert/ConvertiblePair.java create mode 100644 core/src/main/java/com/taobao/arthas/core/env/convert/ObjectToStringConverter.java create mode 100644 core/src/main/java/com/taobao/arthas/core/env/convert/StringToArrayConverter.java create mode 100644 core/src/main/java/com/taobao/arthas/core/env/convert/StringToBooleanConverter.java create mode 100644 core/src/main/java/com/taobao/arthas/core/env/convert/StringToEnumConverter.java create mode 100644 core/src/main/java/com/taobao/arthas/core/env/convert/StringToInetAddressConverter.java create mode 100644 core/src/main/java/com/taobao/arthas/core/env/convert/StringToIntegerConverter.java create mode 100644 core/src/main/java/com/taobao/arthas/core/env/convert/StringToLongConverter.java create mode 100644 core/src/test/java/com/taobao/arthas/core/config/ErrorProperties.java create mode 100644 core/src/test/java/com/taobao/arthas/core/config/PropertiesInjectUtilTest.java create mode 100644 core/src/test/java/com/taobao/arthas/core/config/Server.java create mode 100644 core/src/test/java/com/taobao/arthas/core/config/ServerProperties.java create mode 100644 core/src/test/java/com/taobao/arthas/core/config/ServerPropertiesTest.java create mode 100644 core/src/test/java/com/taobao/arthas/core/config/Ssl.java create mode 100644 core/src/test/java/com/taobao/arthas/core/config/SystemObject.java diff --git a/core/pom.xml b/core/pom.xml index 5b0813aef..4a9a9ba9b 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -134,6 +134,10 @@ com.taobao.middleware logger.api + + com.alibaba.arthas + arthas-repackage-logger + log4j diff --git a/core/src/main/java/com/taobao/arthas/core/config/BinderUtils.java b/core/src/main/java/com/taobao/arthas/core/config/BinderUtils.java new file mode 100644 index 000000000..fe52eb441 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/config/BinderUtils.java @@ -0,0 +1,110 @@ +package com.taobao.arthas.core.config; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import com.taobao.arthas.core.env.Environment; + +/** + * + * @author hengyunabc 2020-01-10 + * + */ +public class BinderUtils { + + public static void inject(Environment environment, Object instance) { + inject(environment, null, null, instance); + } + + public static void inject(Environment environment, String prefix, Object instance) { + inject(environment, null, prefix, instance); + } + + public static void inject(Environment environment, String parentPrefix, String prefix, Object instance) { + Class type = instance.getClass(); + try { + Config annotation = type.getAnnotation(Config.class); + + if (prefix == null) { + prefix = ""; + } + + if (annotation == null) { + prefix = parentPrefix + '.' + prefix; + } else { + prefix = annotation.prefix(); + if (prefix != null) { + if (parentPrefix != null && parentPrefix.length() > 0) { + prefix = parentPrefix + '.' + prefix; + } + } + } + + Method[] declaredMethods = type.getDeclaredMethods(); + if (declaredMethods != null) { + // 获取到所有setter方法,再提取出field。根据前缀从 properties里取出值,再尝试用setter方法注入到对象里 + for (Method method : declaredMethods) { + String methodName = method.getName(); + Class[] parameterTypes = method.getParameterTypes(); + + if (parameterTypes != null && parameterTypes.length == 1 && methodName.startsWith("set") + && methodName.length() > "set".length()) { + + String field = getFieldNameFromSetterMethod(methodName); + Object reslovedValue = environment.getProperty(prefix + '.' + field, parameterTypes[0]); + if (reslovedValue != null) { + method.invoke(instance, new Object[] { reslovedValue }); + } + } + } + } + + } catch (Exception e) { + throw new RuntimeException("inject error. prefix: " + prefix + ", instance: " + instance, e); + } + + // process @NestedConfig + Field[] fields = type.getDeclaredFields(); + if (fields != null) { + for (Field field : fields) { + NestedConfig nestedConfig = field.getAnnotation(NestedConfig.class); + if (nestedConfig != null) { + String prefixForField = field.getName(); + if (parentPrefix != null && prefix.length() > 0) { + prefixForField = prefix + '.' + prefixForField; + } + + field.setAccessible(true); + try { + Object fieldValue = field.get(instance); + if (fieldValue == null) { + fieldValue = field.getType().newInstance(); + } + inject(environment, prefix, prefixForField, fieldValue); + + field.set(instance, fieldValue); + } catch (Exception e) { + throw new RuntimeException("process @NestedConfig error, field: " + field + ", prefix: " + + prefix + ", instance: " + instance, e); + } + } + } + } + } + + /** + * 从setter方法获取到field的String。比如 setHost, 则获取到的是host。 + * + * @param methodName + * @return + */ + private static String getFieldNameFromSetterMethod(String methodName) { + String field = methodName.substring("set".length()); + String startPart = field.substring(0, 1).toLowerCase(); + String endPart = field.substring(1); + + field = startPart + endPart; + return field; + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/config/Config.java b/core/src/main/java/com/taobao/arthas/core/config/Config.java new file mode 100644 index 000000000..666b46ce0 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/config/Config.java @@ -0,0 +1,19 @@ +package com.taobao.arthas.core.config; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author hengyunabc 2019-08-05 + * + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface Config { + + String prefix() default ""; + +} \ No newline at end of file diff --git a/core/src/main/java/com/taobao/arthas/core/config/Configure.java b/core/src/main/java/com/taobao/arthas/core/config/Configure.java index d2f91a9fe..17a62d868 100644 --- a/core/src/main/java/com/taobao/arthas/core/config/Configure.java +++ b/core/src/main/java/com/taobao/arthas/core/config/Configure.java @@ -15,6 +15,7 @@ import static java.lang.reflect.Modifier.isStatic; * @author vlinux * @author hengyunabc 2018-11-12 */ +@Config(prefix = "arthas") public class Configure { public static final long DEFAULT_SESSION_TIMEOUT_SECONDS = ShellServerOptions.DEFAULT_SESSION_TIMEOUT/1000; private String ip; @@ -117,9 +118,6 @@ public class Configure { this.statUrl = statUrl; } - // 对象的编码解码器 - private final static FeatureCodec codec = new FeatureCodec(';', '='); - /** * 序列化成字符串 * @@ -148,7 +146,7 @@ public class Configure { } - return codec.toString(map); + return FeatureCodec.DEFAULT_COMMANDLINE_CODEC.toString(map); } /** @@ -159,7 +157,7 @@ public class Configure { */ public static Configure toConfigure(String toString) throws IllegalAccessException { final Configure configure = new Configure(); - final Map map = codec.toMap(toString); + final Map map = FeatureCodec.DEFAULT_COMMANDLINE_CODEC.toMap(toString); for (Map.Entry entry : map.entrySet()) { final Field field = ArthasReflectUtils.getField(Configure.class, entry.getKey()); diff --git a/core/src/main/java/com/taobao/arthas/core/config/FeatureCodec.java b/core/src/main/java/com/taobao/arthas/core/config/FeatureCodec.java index fa9c2e354..8d5cec807 100644 --- a/core/src/main/java/com/taobao/arthas/core/config/FeatureCodec.java +++ b/core/src/main/java/com/taobao/arthas/core/config/FeatureCodec.java @@ -16,6 +16,8 @@ import static com.taobao.arthas.core.util.StringUtils.isBlank; * Created by dukun on 15/3/31. */ public class FeatureCodec { + // 对象的编码解码器 + public final static FeatureCodec DEFAULT_COMMANDLINE_CODEC = new FeatureCodec(';', '='); /** * KV片段分割符
diff --git a/core/src/main/java/com/taobao/arthas/core/config/NestedConfig.java b/core/src/main/java/com/taobao/arthas/core/config/NestedConfig.java new file mode 100644 index 000000000..1c48590ef --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/config/NestedConfig.java @@ -0,0 +1,17 @@ +package com.taobao.arthas.core.config; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author hengyunabc 2019-08-05 + * + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface NestedConfig { + +} \ No newline at end of file diff --git a/core/src/main/java/com/taobao/arthas/core/config/SecondConfig.java b/core/src/main/java/com/taobao/arthas/core/config/SecondConfig.java new file mode 100644 index 000000000..013a45790 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/config/SecondConfig.java @@ -0,0 +1,5 @@ +package com.taobao.arthas.core.config; + +public class SecondConfig { + +} diff --git a/core/src/main/java/com/taobao/arthas/core/config/TestConfig.java b/core/src/main/java/com/taobao/arthas/core/config/TestConfig.java new file mode 100644 index 000000000..2145f47ca --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/config/TestConfig.java @@ -0,0 +1,9 @@ +package com.taobao.arthas.core.config; + + +@Config +public class TestConfig { + + @NestedConfig + SecondConfig secondConfig; +} 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 index 9cc715aa8..5ccda39c3 100644 --- a/core/src/main/java/com/taobao/arthas/core/env/AbstractPropertyResolver.java +++ b/core/src/main/java/com/taobao/arthas/core/env/AbstractPropertyResolver.java @@ -19,6 +19,9 @@ package com.taobao.arthas.core.env; import java.util.LinkedHashSet; import java.util.Set; +import com.taobao.arthas.core.env.convert.ConfigurableConversionService; +import com.taobao.arthas.core.env.convert.DefaultConversionService; + /** * Abstract base class for resolving properties against any underlying source. * 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 index d7bfccdf5..72186b8cb 100644 --- a/core/src/main/java/com/taobao/arthas/core/env/ArthasEnvironment.java +++ b/core/src/main/java/com/taobao/arthas/core/env/ArthasEnvironment.java @@ -27,6 +27,13 @@ public class ArthasEnvironment implements Environment { new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment())); } + /** + * Add the given property source object with highest precedence. + */ + public void addFirst(PropertySource propertySource) { + this.propertySources.addFirst(propertySource); + } + /** * Add the given property source object with lowest precedence. */ 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 index c0f63ae68..8960ea094 100644 --- a/core/src/main/java/com/taobao/arthas/core/env/ConfigurablePropertyResolver.java +++ b/core/src/main/java/com/taobao/arthas/core/env/ConfigurablePropertyResolver.java @@ -16,6 +16,8 @@ package com.taobao.arthas.core.env; +import com.taobao.arthas.core.env.convert.ConfigurableConversionService; + /** * Configuration interface to be implemented by most if not all * {@link PropertyResolver} types. Provides facilities for accessing and diff --git a/core/src/main/java/com/taobao/arthas/core/env/convert/ConfigurableConversionService.java b/core/src/main/java/com/taobao/arthas/core/env/convert/ConfigurableConversionService.java index 28ff54841..9ba27ee44 100644 --- a/core/src/main/java/com/taobao/arthas/core/env/convert/ConfigurableConversionService.java +++ b/core/src/main/java/com/taobao/arthas/core/env/convert/ConfigurableConversionService.java @@ -14,7 +14,9 @@ * limitations under the License. */ -package com.taobao.arthas.core.env; +package com.taobao.arthas.core.env.convert; + +import com.taobao.arthas.core.env.ConversionService; /** * Configuration interface to be implemented by most if not all @@ -28,7 +30,7 @@ package com.taobao.arthas.core.env; * * @author Chris Beams * @since 3.1 - * @see org.springframework.core.env.ConfigurablePropertyResolver#getConversionService() + * @see com.taobao.arthas.core.env.springframework.core.env.ConfigurablePropertyResolver#getConversionService() * @see org.springframework.core.env.ConfigurableEnvironment * @see org.springframework.context.ConfigurableApplicationContext#getEnvironment() */ diff --git a/core/src/main/java/com/taobao/arthas/core/env/convert/Converter.java b/core/src/main/java/com/taobao/arthas/core/env/convert/Converter.java new file mode 100644 index 000000000..2ab514626 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/env/convert/Converter.java @@ -0,0 +1,40 @@ +/* + * 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 + * + * 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.convert; + +/** + * A converter converts a source object of type S to a target of type T. + * Implementations of this interface are thread-safe and can be shared. + * + *

Implementations may additionally implement {@link ConditionalConverter}. + * + * @author Keith Donald + * @since 3.0 + * @param The source type + * @param The target type + */ +public interface Converter { + + /** + * Convert the source of type S to target type T. + * @param source the source object to convert, which must be an instance of S (never {@code null}) + * @return the converted object, which must be an instance of T (potentially {@code null}) + * @throws IllegalArgumentException if the source could not be converted to the desired target type + */ + T convert(S source, Class targetType); + +} diff --git a/core/src/main/java/com/taobao/arthas/core/env/convert/ConvertiblePair.java b/core/src/main/java/com/taobao/arthas/core/env/convert/ConvertiblePair.java new file mode 100644 index 000000000..d792db621 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/env/convert/ConvertiblePair.java @@ -0,0 +1,52 @@ +package com.taobao.arthas.core.env.convert; + +/** + * Holder for a source-to-target class pair. + */ +public final class ConvertiblePair { + + private final Class sourceType; + + private final Class targetType; + + /** + * Create a new source-to-target pair. + * + * @param sourceType the source type + * @param targetType the target type + */ + public ConvertiblePair(Class sourceType, Class targetType) { + this.sourceType = sourceType; + this.targetType = targetType; + } + + public Class getSourceType() { + return this.sourceType; + } + + public Class getTargetType() { + return this.targetType; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || obj.getClass() != ConvertiblePair.class) { + return false; + } + ConvertiblePair other = (ConvertiblePair) obj; + return this.sourceType.equals(other.sourceType) && this.targetType.equals(other.targetType); + } + + @Override + public int hashCode() { + return this.sourceType.hashCode() * 31 + this.targetType.hashCode(); + } + + @Override + public String toString() { + return this.sourceType.getName() + " -> " + this.targetType.getName(); + } +} \ No newline at end of file diff --git a/core/src/main/java/com/taobao/arthas/core/env/convert/DefaultConversionService.java b/core/src/main/java/com/taobao/arthas/core/env/convert/DefaultConversionService.java index 462ce4bd3..cc31e5417 100644 --- a/core/src/main/java/com/taobao/arthas/core/env/convert/DefaultConversionService.java +++ b/core/src/main/java/com/taobao/arthas/core/env/convert/DefaultConversionService.java @@ -1,17 +1,123 @@ -package com.taobao.arthas.core.env; +package com.taobao.arthas.core.env.convert; + +import java.lang.reflect.Array; +import java.net.InetAddress; +import java.util.Arrays; +import java.util.concurrent.ConcurrentHashMap; public class DefaultConversionService implements ConfigurableConversionService { + private static ConcurrentHashMap converters = new ConcurrentHashMap(); + + public DefaultConversionService() { + addDefaultConverter(); + + } + + private void addDefaultConverter() { + converters.put(new ConvertiblePair(String.class, Integer.class), new StringToIntegerConverter()); + converters.put(new ConvertiblePair(String.class, Long.class), new StringToLongConverter()); + + converters.put(new ConvertiblePair(String.class, Boolean.class), new StringToBooleanConverter()); + + converters.put(new ConvertiblePair(String.class, InetAddress.class), new StringToInetAddressConverter()); + + converters.put(new ConvertiblePair(String.class, Enum.class), new StringToEnumConverter()); + + converters.put(new ConvertiblePair(String.class, Arrays.class), new StringToArrayConverter(this)); + + } + @Override public boolean canConvert(Class sourceType, Class targetType) { - // TODO Auto-generated method stub - return true; + if (sourceType == targetType) { + return true; + } + + if (targetType.isPrimitive()) { + targetType = objectiveClass(targetType); + } + + if (converters.containsKey(new ConvertiblePair(sourceType, targetType))) { + return true; + } + if (targetType.isEnum()) { + if (converters.containsKey(new ConvertiblePair(sourceType, Enum.class))) { + return true; + } + } + + if (targetType.isArray()) { + return true; + } + return false; } @Override public T convert(Object source, Class targetType) { - // TODO Auto-generated method stub + + if (targetType.isPrimitive()) { + targetType = (Class) objectiveClass(targetType); + } + + Converter converter = converters.get(new ConvertiblePair(source.getClass(), targetType)); + + if (converter == null && targetType.isArray()) { + converter = converters.get(new ConvertiblePair(source.getClass(), Arrays.class)); + } + + if (converter == null && targetType.isEnum()) { + converter = converters.get(new ConvertiblePair(source.getClass(), Enum.class)); + } + if (converter != null) { + return (T) converter.convert(source, targetType); + } + return (T) source; } + /** + * Get an array class of the given class. + * + * @param klass to get an array class of + * @param the targeted class + * @return an array class of the given class + */ + public static Class arrayClass(Class klass) { + return (Class) Array.newInstance(klass, 0).getClass(); + } + + /** + * Get the class that extends {@link Object} that represent the given class. + * + * @param klass to get the object class of + * @return the class that extends Object class and represent the given class + */ + public static Class objectiveClass(Class klass) { + Class component = klass.getComponentType(); + if (component != null) { + if (component.isPrimitive() || component.isArray()) + return arrayClass(objectiveClass(component)); + } else if (klass.isPrimitive()) { + if (klass == char.class) + return Character.class; + if (klass == int.class) + return Integer.class; + if (klass == boolean.class) + return Boolean.class; + if (klass == byte.class) + return Byte.class; + if (klass == double.class) + return Double.class; + if (klass == float.class) + return Float.class; + if (klass == long.class) + return Long.class; + if (klass == short.class) + return Short.class; + } + + return klass; + } + } diff --git a/core/src/main/java/com/taobao/arthas/core/env/convert/ObjectToStringConverter.java b/core/src/main/java/com/taobao/arthas/core/env/convert/ObjectToStringConverter.java new file mode 100644 index 000000000..cb5f4059a --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/env/convert/ObjectToStringConverter.java @@ -0,0 +1,31 @@ +/* + * 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.convert; + +/** + * Simply calls {@link Object#toString()} to convert a source Object to a String. + * + * @author Keith Donald + * @since 3.0 + */ +final class ObjectToStringConverter implements Converter { + + public String convert(Object source, Class targetType) { + return source.toString(); + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/env/convert/StringToArrayConverter.java b/core/src/main/java/com/taobao/arthas/core/env/convert/StringToArrayConverter.java new file mode 100644 index 000000000..2b4983f6f --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/env/convert/StringToArrayConverter.java @@ -0,0 +1,33 @@ + +package com.taobao.arthas.core.env.convert; + +import java.lang.reflect.Array; + +import com.taobao.arthas.core.env.ConversionService; +import com.taobao.arthas.core.util.StringUtils; + +final class StringToArrayConverter implements Converter { + + private ConversionService conversionService; + + public StringToArrayConverter(ConversionService conversionService) { + this.conversionService = conversionService; + } + + @Override + public T[] convert(String source, Class targetType) { + String[] strings = StringUtils.tokenizeToStringArray(source, ","); + + @SuppressWarnings("unchecked") + T[] values = (T[]) Array.newInstance(targetType.getComponentType(), strings.length); + for (int i = 0; i < strings.length; ++i) { + @SuppressWarnings("unchecked") + T value = (T) conversionService.convert(strings[i], targetType.getComponentType()); + + values[i] = value; + } + + return values; + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/env/convert/StringToBooleanConverter.java b/core/src/main/java/com/taobao/arthas/core/env/convert/StringToBooleanConverter.java new file mode 100644 index 000000000..bde55d05b --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/env/convert/StringToBooleanConverter.java @@ -0,0 +1,64 @@ +/* + * 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 + * + * 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.convert; + +import java.util.HashSet; +import java.util.Set; + +/** + * Converts String to a Boolean. + * + * @author Keith Donald + * @author Juergen Hoeller + * @since 3.0 + */ +final class StringToBooleanConverter implements Converter { + + private static final Set trueValues = new HashSet(4); + + private static final Set falseValues = new HashSet(4); + + static { + trueValues.add("true"); + trueValues.add("on"); + trueValues.add("yes"); + trueValues.add("1"); + + falseValues.add("false"); + falseValues.add("off"); + falseValues.add("no"); + falseValues.add("0"); + } + + public Boolean convert(String source, Class targetType) { + String value = source.trim(); + if ("".equals(value)) { + return null; + } + value = value.toLowerCase(); + if (trueValues.contains(value)) { + return Boolean.TRUE; + } + else if (falseValues.contains(value)) { + return Boolean.FALSE; + } + else { + throw new IllegalArgumentException("Invalid boolean value '" + source + "'"); + } + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/env/convert/StringToEnumConverter.java b/core/src/main/java/com/taobao/arthas/core/env/convert/StringToEnumConverter.java new file mode 100644 index 000000000..1e6321e1d --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/env/convert/StringToEnumConverter.java @@ -0,0 +1,12 @@ + +package com.taobao.arthas.core.env.convert; + +@SuppressWarnings("rawtypes") +final class StringToEnumConverter implements Converter { + @SuppressWarnings("unchecked") + @Override + public T convert(String source, Class targetType) { + return (T) Enum.valueOf(targetType, source); + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/env/convert/StringToInetAddressConverter.java b/core/src/main/java/com/taobao/arthas/core/env/convert/StringToInetAddressConverter.java new file mode 100644 index 000000000..7b17d77c4 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/env/convert/StringToInetAddressConverter.java @@ -0,0 +1,17 @@ +package com.taobao.arthas.core.env.convert; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +public class StringToInetAddressConverter implements Converter { + + @Override + public InetAddress convert(String source, Class targetType) { + try { + return InetAddress.getByName(source); + } catch (UnknownHostException e) { + throw new IllegalArgumentException("Invalid InetAddress value '" + source + "'", e); + } + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/env/convert/StringToIntegerConverter.java b/core/src/main/java/com/taobao/arthas/core/env/convert/StringToIntegerConverter.java new file mode 100644 index 000000000..726bfd48e --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/env/convert/StringToIntegerConverter.java @@ -0,0 +1,9 @@ + +package com.taobao.arthas.core.env.convert; + +final class StringToIntegerConverter implements Converter { + @Override + public Integer convert(String source, Class targetType) { + return Integer.parseInt(source); + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/env/convert/StringToLongConverter.java b/core/src/main/java/com/taobao/arthas/core/env/convert/StringToLongConverter.java new file mode 100644 index 000000000..3503498b5 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/env/convert/StringToLongConverter.java @@ -0,0 +1,9 @@ + +package com.taobao.arthas.core.env.convert; + +final class StringToLongConverter implements Converter { + @Override + public Long convert(String source, Class targetType) { + return Long.parseLong(source); + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java index cfcc12833..b19fa7084 100644 --- a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java +++ b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java @@ -1,6 +1,9 @@ package com.taobao.arthas.core.server; import com.taobao.arthas.core.config.Configure; +import com.taobao.arthas.core.config.FeatureCodec; +import com.taobao.arthas.core.env.ArthasEnvironment; +import com.taobao.arthas.core.env.MapPropertySource; import com.alibaba.arthas.tunnel.client.TunnelClient; import com.taobao.arthas.common.PidUtils; import com.taobao.arthas.core.advisor.AdviceWeaver; @@ -30,6 +33,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; @@ -45,6 +49,8 @@ public class ArthasBootstrap { private static Logger logger = LogUtil.getArthasLogger(); private static ArthasBootstrap arthasBootstrap; + private ArthasEnvironment arthasEnvironment; + private AtomicBoolean isBindRef = new AtomicBoolean(false); private Instrumentation instrumentation; private Thread shutdown; @@ -95,7 +101,21 @@ public class ArthasBootstrap { public void bind(String args) throws Throwable { initSpy(); + + if( arthasEnvironment == null) { + arthasEnvironment = new ArthasEnvironment(); + } Configure configure = Configure.toConfigure(args); + Map argsMap = FeatureCodec.DEFAULT_COMMANDLINE_CODEC.toMap(args); + // 给配置全加上前缀 + Map mapWithPrefix = new HashMap(argsMap.size()); + for(Entry entry : argsMap.entrySet()) { + mapWithPrefix.put("arthas." + entry.getKey(), entry.getValue()); + } + + MapPropertySource mapPropertySource = new MapPropertySource("args", mapWithPrefix); + arthasEnvironment.addFirst(mapPropertySource); + bind(configure); } diff --git a/core/src/test/java/com/taobao/arthas/core/config/ErrorProperties.java b/core/src/test/java/com/taobao/arthas/core/config/ErrorProperties.java new file mode 100644 index 000000000..2019d72b6 --- /dev/null +++ b/core/src/test/java/com/taobao/arthas/core/config/ErrorProperties.java @@ -0,0 +1,62 @@ +package com.taobao.arthas.core.config; + +public class ErrorProperties { + + /** + * Path of the error controller. + */ + // @Value("${error.path:/error}") + private String path = "/error"; + + /** + * When to include a "stacktrace" attribute. + */ + private IncludeStacktrace includeStacktrace = IncludeStacktrace.NEVER; + + public String getPath() { + return this.path; + } + + public void setPath(String path) { + this.path = path; + } + + public IncludeStacktrace getIncludeStacktrace() { + return this.includeStacktrace; + } + + public void setIncludeStacktrace(IncludeStacktrace includeStacktrace) { + this.includeStacktrace = includeStacktrace; + } + + + @Override + public String toString() { + return "ErrorProperties [path=" + path + ", includeStacktrace=" + includeStacktrace + "]"; + } + + + /** + * Include Stacktrace attribute options. + */ + public enum IncludeStacktrace { + + /** + * Never add stacktrace information. + */ + NEVER, + + /** + * Always add stacktrace information. + */ + ALWAYS, + + /** + * Add stacktrace information when the "trace" request parameter is + * "true". + */ + ON_TRACE_PARAM + + } + +} \ No newline at end of file diff --git a/core/src/test/java/com/taobao/arthas/core/config/PropertiesInjectUtilTest.java b/core/src/test/java/com/taobao/arthas/core/config/PropertiesInjectUtilTest.java new file mode 100644 index 000000000..68c24dd44 --- /dev/null +++ b/core/src/test/java/com/taobao/arthas/core/config/PropertiesInjectUtilTest.java @@ -0,0 +1,66 @@ +package com.taobao.arthas.core.config; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Properties; + +import org.junit.Assert; +import org.junit.Test; + +import com.taobao.arthas.core.config.ErrorProperties.IncludeStacktrace; +import com.taobao.arthas.core.env.ArthasEnvironment; +import com.taobao.arthas.core.env.PropertiesPropertySource; + + + +public class PropertiesInjectUtilTest { + + @Test + public void test() throws UnknownHostException { + Properties p = new Properties(); + p.put("server.port", "8080"); + + p.put("server.host", "localhost"); + p.put("server.flag", "true"); + + p.put("server.address", "192.168.1.1"); + + p.put("server.ssl.enabled", "true"); + p.put("server.ssl.protocol", "TLS"); + p.put("server.ssl.testBoolean", "false"); + p.put("server.ssl.testLong", "123"); + p.put("server.ssl.ciphers", "abc, efg ,hij"); + + p.put("server.error.includeStacktrace", "ALWAYS"); + + + ArthasEnvironment arthasEnvironment = new ArthasEnvironment(); + + arthasEnvironment.addLast(new PropertiesPropertySource("test1", p)); + + + Server server = new Server(); + + BinderUtils.inject(arthasEnvironment, server); + + System.out.println(server); + + Assert.assertEquals(server.getPort(), 8080); + Assert.assertEquals(server.getHost(), "localhost"); + Assert.assertTrue(server.isFlag()); + + Assert.assertEquals(server.getAddress(), InetAddress.getByName("192.168.1.1")); + + Assert.assertEquals(server.ssl.getProtocol(), "TLS"); + Assert.assertTrue(server.ssl.isEnabled()); + Assert.assertFalse(server.ssl.getTestBoolean()); + Assert.assertEquals(server.ssl.getTestLong(), Long.valueOf(123)); + Assert.assertNull(server.ssl.getTestDouble()); + + Assert.assertArrayEquals(server.ssl.getCiphers(), new String[] { "abc", "efg", "hij" }); + + Assert.assertEquals(server.error.getIncludeStacktrace(), IncludeStacktrace.ALWAYS); + + } + +} diff --git a/core/src/test/java/com/taobao/arthas/core/config/Server.java b/core/src/test/java/com/taobao/arthas/core/config/Server.java new file mode 100644 index 000000000..cda0f85c0 --- /dev/null +++ b/core/src/test/java/com/taobao/arthas/core/config/Server.java @@ -0,0 +1,59 @@ +package com.taobao.arthas.core.config; + +import java.net.InetAddress; + +@Config(prefix = "server") +public class Server { + + int port; + String host; + + InetAddress address; + + boolean flag; + + @NestedConfig + Ssl ssl; + + @NestedConfig + ErrorProperties error; + + public InetAddress getAddress() { + return address; + } + + public void setAddress(InetAddress address) { + this.address = address; + } + + public boolean isFlag() { + return flag; + } + + public void setFlag(boolean flag) { + this.flag = flag; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + @Override + public String toString() { + return "Server [port=" + port + ", host=" + host + ", address=" + address + ", flag=" + flag + ", ssl=" + ssl + + ", error=" + error + "]"; + } + +} \ No newline at end of file diff --git a/core/src/test/java/com/taobao/arthas/core/config/ServerProperties.java b/core/src/test/java/com/taobao/arthas/core/config/ServerProperties.java new file mode 100644 index 000000000..8d6b55a22 --- /dev/null +++ b/core/src/test/java/com/taobao/arthas/core/config/ServerProperties.java @@ -0,0 +1,81 @@ +package com.taobao.arthas.core.config; + +import java.net.InetAddress; + +@Config(prefix = "server") +public class ServerProperties { + + /** + * Server HTTP port. + */ + private Integer port; + + /** + * Network address to which the server should bind to. + */ + private InetAddress address; + + /** + * Context path of the application. + */ + private String contextPath; + + /** + * Display name of the application. + */ + private String displayName = "application"; + + @NestedConfig + private ErrorProperties error = new ErrorProperties(); + + @NestedConfig + private Ssl ssl; + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public InetAddress getAddress() { + return address; + } + + public void setAddress(InetAddress address) { + this.address = address; + } + + public String getContextPath() { + return contextPath; + } + + public void setContextPath(String contextPath) { + this.contextPath = contextPath; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public ErrorProperties getError() { + return error; + } + + public void setError(ErrorProperties error) { + this.error = error; + } + + public Ssl getSsl() { + return ssl; + } + + public void setSsl(Ssl ssl) { + this.ssl = ssl; + } +} diff --git a/core/src/test/java/com/taobao/arthas/core/config/ServerPropertiesTest.java b/core/src/test/java/com/taobao/arthas/core/config/ServerPropertiesTest.java new file mode 100644 index 000000000..f74bd7ebd --- /dev/null +++ b/core/src/test/java/com/taobao/arthas/core/config/ServerPropertiesTest.java @@ -0,0 +1,76 @@ +package com.taobao.arthas.core.config; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Properties; + +import org.junit.Assert; +import org.junit.Test; + +import com.taobao.arthas.core.config.ErrorProperties.IncludeStacktrace; +import com.taobao.arthas.core.env.ArthasEnvironment; +import com.taobao.arthas.core.env.PropertiesPropertySource; + + +public class ServerPropertiesTest { + + @Test + public void test() throws UnknownHostException { + Properties p = new Properties(); + p.put("server.port", "8080"); + + p.put("server.address", "192.168.1.1"); + + p.put("server.ssl.enabled", "true"); + p.put("server.ssl.protocol", "TLS"); + p.put("server.ssl.ciphers", "abc, efg ,hij"); + + p.put("server.error.includeStacktrace", "ALWAYS"); + + + ArthasEnvironment arthasEnvironment = new ArthasEnvironment(); + + arthasEnvironment.addLast(new PropertiesPropertySource("test1", p)); + + ServerProperties serverProperties = new ServerProperties(); + + BinderUtils.inject(arthasEnvironment, serverProperties); + + Assert.assertEquals(serverProperties.getPort().intValue(), 8080); + + Assert.assertEquals(serverProperties.getAddress(), InetAddress.getByName("192.168.1.1")); + + Assert.assertEquals(serverProperties.getSsl().getProtocol(), "TLS"); + Assert.assertTrue(serverProperties.getSsl().isEnabled()); + + Assert.assertArrayEquals(serverProperties.getSsl().getCiphers(), new String[] { "abc", "efg", "hij" }); + + Assert.assertEquals(serverProperties.getError().getIncludeStacktrace(), IncludeStacktrace.ALWAYS); + + } + + @Test + public void testSystemProperties() { + Properties p = new Properties(); + p.put("system.test.systemKey", "kkk"); + p.put("system.test.nonSystemKey", "xxxx"); + p.put("system.test.systemIngeger", "123"); + + System.setProperty("system.test.systemKey", "ssss"); + System.setProperty("system.test.systemIngeger", "110"); + + ArthasEnvironment arthasEnvironment = new ArthasEnvironment(); + + arthasEnvironment.addLast(new PropertiesPropertySource("test1", p)); + + SystemObject systemObject = new SystemObject(); + + BinderUtils.inject(arthasEnvironment, systemObject); + + Assert.assertEquals(systemObject.getSystemKey(), "ssss"); + Assert.assertEquals(systemObject.getNonSystemKey(), "xxxx"); + Assert.assertEquals(systemObject.getSystemIngeger(), 110); + + } + +} diff --git a/core/src/test/java/com/taobao/arthas/core/config/Ssl.java b/core/src/test/java/com/taobao/arthas/core/config/Ssl.java new file mode 100644 index 000000000..ba2831d4b --- /dev/null +++ b/core/src/test/java/com/taobao/arthas/core/config/Ssl.java @@ -0,0 +1,72 @@ +package com.taobao.arthas.core.config; + +import java.util.Arrays; + +public class Ssl { + + String protocol; + boolean enabled; + + Boolean testBoolean; + + Long testLong; + + Double testDouble; + + String[] ciphers; + + public String[] getCiphers() { + return ciphers; + } + + public void setCiphers(String[] ciphers) { + this.ciphers = ciphers; + } + + public Long getTestLong() { + return testLong; + } + + public void setTestLong(Long testLong) { + this.testLong = testLong; + } + + public Double getTestDouble() { + return testDouble; + } + + public void setTestDouble(Double testDouble) { + this.testDouble = testDouble; + } + + public Boolean getTestBoolean() { + return testBoolean; + } + + public void setTestBoolean(Boolean testBoolean) { + this.testBoolean = testBoolean; + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + @Override + public String toString() { + return "Ssl [protocol=" + protocol + ", enabled=" + enabled + ", testBoolean=" + testBoolean + ", testLong=" + + testLong + ", testDouble=" + testDouble + ", ciphers=" + Arrays.toString(ciphers) + "]"; + } + +} \ No newline at end of file diff --git a/core/src/test/java/com/taobao/arthas/core/config/SystemObject.java b/core/src/test/java/com/taobao/arthas/core/config/SystemObject.java new file mode 100644 index 000000000..e785af0c9 --- /dev/null +++ b/core/src/test/java/com/taobao/arthas/core/config/SystemObject.java @@ -0,0 +1,34 @@ +package com.taobao.arthas.core.config; + +@Config(prefix = "system.test") +public class SystemObject { + String systemKey; + + int systemIngeger; + + String nonSystemKey; + + public String getSystemKey() { + return systemKey; + } + + public void setSystemKey(String systemKey) { + this.systemKey = systemKey; + } + + public int getSystemIngeger() { + return systemIngeger; + } + + public void setSystemIngeger(int systemIngeger) { + this.systemIngeger = systemIngeger; + } + + public String getNonSystemKey() { + return nonSystemKey; + } + + public void setNonSystemKey(String nonSystemKey) { + this.nonSystemKey = nonSystemKey; + } +} diff --git a/pom.xml b/pom.xml index cd6ca18f4..b607fa7b8 100644 --- a/pom.xml +++ b/pom.xml @@ -134,6 +134,11 @@ logger.api 0.2.8 + + com.alibaba.arthas + arthas-repackage-logger + 0.0.1 + com.alibaba fastjson