mirror of https://github.com/longtai-cn/hippo4j
[Enhancement] Log printing failure problem in agent mode (#1513)
* Feature: server add Ldap user authentication * Fix Server-example Unknown user * Remove RFC7230Config.class * Fix Log printing failure problem in agent mode * Remove import * * fix: The Gson version dependency in pom under the infra package is provideddependabot/npm_and_yarn/docs/webpack-5.94.0
parent
41efe403e0
commit
d687133dd2
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You 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 cn.hippo4j.common.boot;
|
||||||
|
|
||||||
|
import cn.hippo4j.common.logging.api.ILog;
|
||||||
|
import cn.hippo4j.common.logging.api.LogManager;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AgentPackagePath is a flag and finder to locate the Hippo4j agent.jar. It gets the absolute path of the agent jar.
|
||||||
|
* The path is the required metadata for agent core looking up the plugins and toolkit activations. If the lookup
|
||||||
|
* mechanism fails, the agent will exit directly.
|
||||||
|
*/
|
||||||
|
public class AgentPackagePath {
|
||||||
|
|
||||||
|
private static final ILog LOGGER = LogManager.getLogger(AgentPackagePath.class);
|
||||||
|
|
||||||
|
private static File AGENT_PACKAGE_PATH;
|
||||||
|
|
||||||
|
public static File getPath() throws AgentPackageNotFoundException {
|
||||||
|
if (AGENT_PACKAGE_PATH == null) {
|
||||||
|
AGENT_PACKAGE_PATH = findPath();
|
||||||
|
}
|
||||||
|
return AGENT_PACKAGE_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isPathFound() {
|
||||||
|
return AGENT_PACKAGE_PATH != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static File findPath() throws AgentPackageNotFoundException {
|
||||||
|
String classResourcePath = AgentPackagePath.class.getName().replaceAll("\\.", "/") + ".class";
|
||||||
|
|
||||||
|
URL resource = ClassLoader.getSystemClassLoader().getResource(classResourcePath);
|
||||||
|
if (resource != null) {
|
||||||
|
String urlString = resource.toString();
|
||||||
|
|
||||||
|
LOGGER.debug("The beacon class location is {}.", urlString);
|
||||||
|
|
||||||
|
int insidePathIndex = urlString.indexOf('!');
|
||||||
|
boolean isInJar = insidePathIndex > -1;
|
||||||
|
|
||||||
|
if (isInJar) {
|
||||||
|
urlString = urlString.substring(urlString.indexOf("file:"), insidePathIndex);
|
||||||
|
File agentJarFile = null;
|
||||||
|
try {
|
||||||
|
agentJarFile = new File(new URL(urlString).toURI());
|
||||||
|
} catch (MalformedURLException | URISyntaxException e) {
|
||||||
|
LOGGER.error(e, "Can not locate agent jar file by url:" + urlString);
|
||||||
|
}
|
||||||
|
if (agentJarFile.exists()) {
|
||||||
|
return agentJarFile.getParentFile();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int prefixLength = "file:".length();
|
||||||
|
String classLocation = urlString.substring(
|
||||||
|
prefixLength, urlString.length() - classResourcePath.length());
|
||||||
|
return new File(classLocation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGGER.error("Can not locate agent jar file.");
|
||||||
|
throw new AgentPackageNotFoundException("Can not locate agent jar file.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You 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 cn.hippo4j.common.conf;
|
||||||
|
|
||||||
|
public class Constants {
|
||||||
|
|
||||||
|
public static String PATH_SEPARATOR = System.getProperty("file.separator", "/");
|
||||||
|
|
||||||
|
public static String LINE_SEPARATOR = System.getProperty("line.separator", "\n");
|
||||||
|
|
||||||
|
public static String EMPTY_STRING = "";
|
||||||
|
|
||||||
|
public static char SERVICE_NAME_PART_CONNECTOR = '|';
|
||||||
|
|
||||||
|
// The name of the layer that represents agent-installed services,
|
||||||
|
// which is defined at
|
||||||
|
// https://github.com/apache/skywalking/blob/85ce1645be53e46286f36c0ea206c60db2d1a716/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/Layer.java#L30
|
||||||
|
public static String EVENT_LAYER_NAME = "GENERAL";
|
||||||
|
|
||||||
|
public static int NULL_VALUE = 0;
|
||||||
|
|
||||||
|
public static boolean IS_INIT_COMPLETED = false;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,210 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You 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 cn.hippo4j.common.toolkit.agent;
|
||||||
|
|
||||||
|
import cn.hippo4j.common.toolkit.StringUtil;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init a class's static fields by a {@link Properties}, including static fields and static inner classes.
|
||||||
|
* <p>
|
||||||
|
*/
|
||||||
|
public class ConfigInitializer {
|
||||||
|
|
||||||
|
public static void initialize(Properties properties, Class<?> rootConfigType) throws IllegalAccessException {
|
||||||
|
initNextLevel(properties, rootConfigType, new ConfigDesc(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void initialize(Properties properties, Class<?> rootConfigType, boolean isSpringProperties) throws IllegalAccessException {
|
||||||
|
initNextLevel(properties, rootConfigType, new ConfigDesc(), isSpringProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void initNextLevel(Properties properties, Class<?> recentConfigType,
|
||||||
|
ConfigDesc parentDesc, boolean isSpringProperties) throws IllegalArgumentException, IllegalAccessException {
|
||||||
|
for (Field field : recentConfigType.getFields()) {
|
||||||
|
if (Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers())) {
|
||||||
|
String configKey = (parentDesc + "." + (isSpringProperties ? field.getName().replace("_", "-") : field.getName())).toLowerCase();
|
||||||
|
Class<?> type = field.getType();
|
||||||
|
|
||||||
|
if (type.equals(Map.class)) {
|
||||||
|
/*
|
||||||
|
* Map config format is, config_key[map_key]=map_value Such as plugin.opgroup.resttemplate.rule[abc]=/url/path
|
||||||
|
*/
|
||||||
|
// Deduct two generic types of the map
|
||||||
|
ParameterizedType genericType = (ParameterizedType) field.getGenericType();
|
||||||
|
Type[] argumentTypes = genericType.getActualTypeArguments();
|
||||||
|
|
||||||
|
Type keyType = null;
|
||||||
|
Type valueType = null;
|
||||||
|
if (argumentTypes != null && argumentTypes.length == 2) {
|
||||||
|
// Get key type and value type of the map
|
||||||
|
keyType = argumentTypes[0];
|
||||||
|
valueType = argumentTypes[1];
|
||||||
|
}
|
||||||
|
Map map = (Map) field.get(null);
|
||||||
|
// Set the map from config key and properties
|
||||||
|
setForMapType(configKey, map, properties, keyType, valueType);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Others typical field type
|
||||||
|
*/
|
||||||
|
String value = properties.getProperty(configKey);
|
||||||
|
// Convert the value into real type
|
||||||
|
final Length lengthDefine = field.getAnnotation(Length.class);
|
||||||
|
if (lengthDefine != null) {
|
||||||
|
if (value != null && value.length() > lengthDefine.value()) {
|
||||||
|
value = value.substring(0, lengthDefine.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Object convertedValue = convertToTypicalType(type, value);
|
||||||
|
if (convertedValue != null) {
|
||||||
|
field.set(null, convertedValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Class<?> innerConfiguration : recentConfigType.getClasses()) {
|
||||||
|
String simpleName = innerConfiguration.getSimpleName();
|
||||||
|
String description = isSpringProperties ? simpleName.replace("_", "-") : simpleName;
|
||||||
|
parentDesc.append(description);
|
||||||
|
initNextLevel(properties, innerConfiguration, parentDesc, isSpringProperties);
|
||||||
|
parentDesc.removeLastDesc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert string value to typical type.
|
||||||
|
*
|
||||||
|
* @param type type to convert
|
||||||
|
* @param value string value to be converted
|
||||||
|
* @return converted value or null
|
||||||
|
*/
|
||||||
|
private static Object convertToTypicalType(Type type, String value) {
|
||||||
|
if (value == null || type == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object result = null;
|
||||||
|
if (String.class.equals(type)) {
|
||||||
|
result = value;
|
||||||
|
} else if (int.class.equals(type) || Integer.class.equals(type)) {
|
||||||
|
result = Integer.valueOf(value);
|
||||||
|
} else if (long.class.equals(type) || Long.class.equals(type)) {
|
||||||
|
result = Long.valueOf(value);
|
||||||
|
} else if (boolean.class.equals(type) || Boolean.class.equals(type)) {
|
||||||
|
result = Boolean.valueOf(value);
|
||||||
|
} else if (float.class.equals(type) || Float.class.equals(type)) {
|
||||||
|
result = Float.valueOf(value);
|
||||||
|
} else if (double.class.equals(type) || Double.class.equals(type)) {
|
||||||
|
result = Double.valueOf(value);
|
||||||
|
} else if (List.class.equals(type)) {
|
||||||
|
result = convert2List(value);
|
||||||
|
} else if (type instanceof Class) {
|
||||||
|
Class<?> clazz = (Class<?>) type;
|
||||||
|
if (clazz.isEnum()) {
|
||||||
|
result = Enum.valueOf((Class<Enum>) type, value.toUpperCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set map items.
|
||||||
|
*
|
||||||
|
* @param configKey config key must not be null
|
||||||
|
* @param map map to set must not be null
|
||||||
|
* @param properties properties must not be null
|
||||||
|
* @param keyType key type of the map
|
||||||
|
* @param valueType value type of the map
|
||||||
|
*/
|
||||||
|
private static void setForMapType(String configKey, Map<Object, Object> map, Properties properties,
|
||||||
|
final Type keyType, final Type valueType) {
|
||||||
|
|
||||||
|
Objects.requireNonNull(configKey);
|
||||||
|
Objects.requireNonNull(map);
|
||||||
|
Objects.requireNonNull(properties);
|
||||||
|
|
||||||
|
String prefix = configKey + "[";
|
||||||
|
String suffix = "]";
|
||||||
|
|
||||||
|
properties.forEach((propertyKey, propertyValue) -> {
|
||||||
|
String propertyStringKey = propertyKey.toString();
|
||||||
|
if (propertyStringKey.startsWith(prefix) && propertyStringKey.endsWith(suffix)) {
|
||||||
|
String itemKey = propertyStringKey.substring(
|
||||||
|
prefix.length(), propertyStringKey.length() - suffix.length());
|
||||||
|
Object keyObj;
|
||||||
|
Object valueObj;
|
||||||
|
|
||||||
|
keyObj = convertToTypicalType(keyType, itemKey);
|
||||||
|
valueObj = convertToTypicalType(valueType, propertyValue.toString());
|
||||||
|
|
||||||
|
if (keyObj == null) {
|
||||||
|
keyObj = itemKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valueObj == null) {
|
||||||
|
valueObj = propertyValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
map.put(keyObj, valueObj);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> convert2List(String value) {
|
||||||
|
if (StringUtil.isEmpty(value)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
List<String> result = new LinkedList<>();
|
||||||
|
|
||||||
|
String[] segments = value.split(",");
|
||||||
|
for (String segment : segments) {
|
||||||
|
String trimmedSegment = segment.trim();
|
||||||
|
if (StringUtil.isNotEmpty(trimmedSegment)) {
|
||||||
|
result.add(trimmedSegment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConfigDesc {
|
||||||
|
|
||||||
|
private LinkedList<String> descs = new LinkedList<>();
|
||||||
|
|
||||||
|
void append(String currentDesc) {
|
||||||
|
if (StringUtil.isNotEmpty(currentDesc)) {
|
||||||
|
descs.addLast(currentDesc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeLastDesc() {
|
||||||
|
descs.removeLast();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.join(".", descs);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You 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 cn.hippo4j.common.toolkit.agent;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The length rule of the target field.
|
||||||
|
*/
|
||||||
|
@Target({ElementType.FIELD})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface Length {
|
||||||
|
|
||||||
|
int value();
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You 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 cn.hippo4j.common.toolkit.agent;
|
||||||
|
|
||||||
|
public class PlaceholderConfigurerSupport {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default placeholder prefix: {@value}
|
||||||
|
*/
|
||||||
|
public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default placeholder suffix: {@value}
|
||||||
|
*/
|
||||||
|
public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value separator: {@value}
|
||||||
|
*/
|
||||||
|
public static final String DEFAULT_VALUE_SEPARATOR = ":";
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,210 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You 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 cn.hippo4j.common.toolkit.agent;
|
||||||
|
|
||||||
|
import cn.hippo4j.common.toolkit.StringUtil;
|
||||||
|
|
||||||
|
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}.
|
||||||
|
*/
|
||||||
|
public enum PropertyPlaceholderHelper {
|
||||||
|
|
||||||
|
INSTANCE(
|
||||||
|
PlaceholderConfigurerSupport.DEFAULT_PLACEHOLDER_PREFIX,
|
||||||
|
PlaceholderConfigurerSupport.DEFAULT_PLACEHOLDER_SUFFIX, PlaceholderConfigurerSupport.DEFAULT_VALUE_SEPARATOR,
|
||||||
|
true);
|
||||||
|
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* @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})
|
||||||
|
*/
|
||||||
|
PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix, String valueSeparator,
|
||||||
|
boolean ignoreUnresolvablePlaceholders) {
|
||||||
|
if (StringUtil.isEmpty(placeholderPrefix) || StringUtil.isEmpty(placeholderSuffix)) {
|
||||||
|
throw new UnsupportedOperationException("'placeholderPrefix or placeholderSuffix' must not be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<String, String> wellKnownSimplePrefixes = new HashMap<String, String>(4);
|
||||||
|
|
||||||
|
wellKnownSimplePrefixes.put("}", "{");
|
||||||
|
wellKnownSimplePrefixes.put("]", "[");
|
||||||
|
wellKnownSimplePrefixes.put(")", "(");
|
||||||
|
|
||||||
|
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() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String resolvePlaceholder(String placeholderName) {
|
||||||
|
return getConfigValue(placeholderName, properties);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getConfigValue(String key, final Properties properties) {
|
||||||
|
String value = System.getProperty(key);
|
||||||
|
if (value == null) {
|
||||||
|
value = System.getenv(key);
|
||||||
|
}
|
||||||
|
if (value == null) {
|
||||||
|
value = properties.getProperty(key);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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, new HashSet<String>());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String parseStringValue(String value, PlaceholderResolver placeholderResolver,
|
||||||
|
Set<String> visitedPlaceholders) {
|
||||||
|
|
||||||
|
StringBuilder result = new StringBuilder(value);
|
||||||
|
|
||||||
|
int startIndex = value.indexOf(this.placeholderPrefix);
|
||||||
|
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.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 (StringUtil.substringMatch(buf, index, this.placeholderSuffix)) {
|
||||||
|
if (withinNestedPlaceholder > 0) {
|
||||||
|
withinNestedPlaceholder--;
|
||||||
|
index = index + this.placeholderSuffix.length();
|
||||||
|
} else {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
} else if (StringUtil.substringMatch(buf, index, this.simplePrefix)) {
|
||||||
|
withinNestedPlaceholder++;
|
||||||
|
index = index + this.simplePrefix.length();
|
||||||
|
} else {
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strategy interface used to resolve replacement values for placeholders contained in Strings.
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You 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 cn.hippo4j.common.toolkit.agent;
|
||||||
|
|
||||||
|
public class RunnableWithExceptionProtection implements Runnable {
|
||||||
|
|
||||||
|
private Runnable run;
|
||||||
|
private CallbackWhenException callback;
|
||||||
|
|
||||||
|
public RunnableWithExceptionProtection(Runnable run, CallbackWhenException callback) {
|
||||||
|
this.run = run;
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
run.run();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
callback.handle(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface CallbackWhenException {
|
||||||
|
|
||||||
|
void handle(Throwable t);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue