【fix】修复ApolloParseHelper.java中调用convert后,可能会出现空指针的问题

【fix】FlowInitHook.java文件中,常量命小写,不符合常用规范,改为了大写
This commit is contained in:
韩华锋
2023-09-14 11:19:42 +08:00
parent c2929f0eba
commit 918111dfda
2 changed files with 177 additions and 170 deletions

View File

@@ -14,20 +14,20 @@ import java.util.function.BooleanSupplier;
*/ */
public class FlowInitHook { public class FlowInitHook {
private static final List<BooleanSupplier> supplierList = new ArrayList<>(); private static final List<BooleanSupplier> SUPPLIER_LIST = new ArrayList<>();
public static void executeHook() { public static void executeHook() {
if (CollUtil.isNotEmpty(supplierList)) { if (CollUtil.isNotEmpty(SUPPLIER_LIST)) {
supplierList.forEach(BooleanSupplier::getAsBoolean); SUPPLIER_LIST.forEach(BooleanSupplier::getAsBoolean);
} }
} }
public static void addHook(BooleanSupplier hookSupplier) { public static void addHook(BooleanSupplier hookSupplier) {
supplierList.add(hookSupplier); SUPPLIER_LIST.add(hookSupplier);
} }
public static void cleanHook() { public static void cleanHook() {
supplierList.clear(); SUPPLIER_LIST.clear();
} }
} }

View File

@@ -24,215 +24,222 @@ import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.ctrip.framework.apollo.enums.PropertyChangeType.DELETED;
/** /**
* @author zhanghua * @author zhanghua
* @since 2.9.5 * @since 2.9.5
*/ */
public class ApolloParseHelper { public class ApolloParseHelper {
private static final Logger LOG = LoggerFactory.getLogger(ApolloParseHelper.class); private static final Logger LOG = LoggerFactory.getLogger(ApolloParseHelper.class);
private final String CHAIN_XML_PATTERN = "<chain name=\"{}\">{}</chain>"; private final String CHAIN_XML_PATTERN = "<chain name=\"{}\">{}</chain>";
private final String NODE_XML_PATTERN = "<nodes>{}</nodes>"; private final String NODE_XML_PATTERN = "<nodes>{}</nodes>";
private final String NODE_ITEM_XML_PATTERN = "<node id=\"{}\" name=\"{}\" type=\"{}\"><![CDATA[{}]]></node>"; private final String NODE_ITEM_XML_PATTERN = "<node id=\"{}\" name=\"{}\" type=\"{}\"><![CDATA[{}]]></node>";
private final String XML_PATTERN = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><flow>{}{}</flow>"; private final String XML_PATTERN = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><flow>{}{}</flow>";
private final ApolloParserConfigVO apolloParserConfigVO; private final ApolloParserConfigVO apolloParserConfigVO;
private Config chainConfig; private Config chainConfig;
private Config scriptConfig; private Config scriptConfig;
public ApolloParseHelper(ApolloParserConfigVO apolloParserConfigVO) { public ApolloParseHelper(ApolloParserConfigVO apolloParserConfigVO) {
this.apolloParserConfigVO = apolloParserConfigVO; this.apolloParserConfigVO = apolloParserConfigVO;
try { try {
try { try {
// 这里本身对于程序运行来说没有什么意义拿到的永远是null // 这里本身对于程序运行来说没有什么意义拿到的永远是null
// 其实config对象也没有注入到spring容器中 // 其实config对象也没有注入到spring容器中
// 这里这样写的目的是为了单测中的mockito当有@MockBean的时候这里就能拿到了 // 这里这样写的目的是为了单测中的mockito当有@MockBean的时候这里就能拿到了
this.chainConfig = ContextAwareHolder.loadContextAware().getBean("chainConfig"); this.chainConfig = ContextAwareHolder.loadContextAware().getBean("chainConfig");
this.scriptConfig = ContextAwareHolder.loadContextAware().getBean("scriptConfig"); this.scriptConfig = ContextAwareHolder.loadContextAware().getBean("scriptConfig");
} } catch (Exception ignored) {
catch (Exception ignored) { }
}
if (ObjectUtil.isNull(chainConfig)) { if (ObjectUtil.isNull(chainConfig)) {
chainConfig = ConfigService.getConfig(apolloParserConfigVO.getChainNamespace()); chainConfig = ConfigService.getConfig(apolloParserConfigVO.getChainNamespace());
String scriptNamespace; String scriptNamespace;
// scriptConfig is optional // scriptConfig is optional
if (StrUtil.isNotBlank(scriptNamespace = apolloParserConfigVO.getScriptNamespace())) { if (StrUtil.isNotBlank(scriptNamespace = apolloParserConfigVO.getScriptNamespace())) {
scriptConfig = ConfigService.getConfig(scriptNamespace); scriptConfig = ConfigService.getConfig(scriptNamespace);
} }
} }
} } catch (Exception e) {
catch (Exception e) { throw new ApolloException(e.getMessage());
throw new ApolloException(e.getMessage()); }
} }
}
public String getContent() { public String getContent() {
try { try {
// 1. handle chain // 1. handle chain
Set<String> propertyNames = chainConfig.getPropertyNames(); Set<String> propertyNames = chainConfig.getPropertyNames();
if (CollectionUtil.isEmpty(propertyNames)) { if (CollectionUtil.isEmpty(propertyNames)) {
throw new ApolloException(StrUtil.format("There are no chains in namespace : {}", throw new ApolloException(StrUtil.format("There are no chains in namespace : {}",
apolloParserConfigVO.getChainNamespace())); apolloParserConfigVO.getChainNamespace()));
} }
List<String> chainItemContentList = propertyNames.stream() List<String> chainItemContentList = propertyNames.stream()
.map(item -> StrUtil.format(CHAIN_XML_PATTERN, item, chainConfig.getProperty(item, StrUtil.EMPTY))) .map(item -> StrUtil.format(CHAIN_XML_PATTERN, item, chainConfig.getProperty(item, StrUtil.EMPTY)))
.collect(Collectors.toList()); .collect(Collectors.toList());
// merge all chain content // merge all chain content
String chainAllContent = CollUtil.join(chainItemContentList, StrUtil.EMPTY); String chainAllContent = CollUtil.join(chainItemContentList, StrUtil.EMPTY);
// 2. handle script if needed // 2. handle script if needed
String scriptAllContent = StrUtil.EMPTY; String scriptAllContent = StrUtil.EMPTY;
Set<String> scriptNamespaces; Set<String> scriptNamespaces;
if (Objects.nonNull(scriptConfig) if (Objects.nonNull(scriptConfig)
&& CollectionUtil.isNotEmpty(scriptNamespaces = scriptConfig.getPropertyNames())) { && CollectionUtil.isNotEmpty(scriptNamespaces = scriptConfig.getPropertyNames())) {
List<String> scriptItemContentList = scriptNamespaces.stream() List<String> scriptItemContentList = scriptNamespaces.stream()
.map(item -> convert(item, scriptConfig.getProperty(item, StrUtil.EMPTY))) .map(item -> convert(item, scriptConfig.getProperty(item, StrUtil.EMPTY)))
.filter(Objects::nonNull) .filter(Objects::nonNull)
.map(item -> StrUtil.format(NODE_ITEM_XML_PATTERN, item.getNodeId(), item.getName(), item.getType(), .map(item -> StrUtil.format(NODE_ITEM_XML_PATTERN, item.getNodeId(), item.getName(), item.getType(),
item.getScript())) item.getScript()))
.collect(Collectors.toList()); .collect(Collectors.toList());
scriptAllContent = StrUtil.format(NODE_XML_PATTERN, scriptAllContent = StrUtil.format(NODE_XML_PATTERN,
CollUtil.join(scriptItemContentList, StrUtil.EMPTY)); CollUtil.join(scriptItemContentList, StrUtil.EMPTY));
} }
return StrUtil.format(XML_PATTERN, scriptAllContent, chainAllContent); return StrUtil.format(XML_PATTERN, scriptAllContent, chainAllContent);
} } catch (Exception e) {
catch (Exception e) { throw new ApolloException(e.getMessage());
throw new ApolloException(e.getMessage()); }
} }
}
/** /**
* listen apollo config change * listen apollo config change
*/ */
public void listenApollo() { public void listenApollo() {
// chain
chainConfig.addChangeListener(changeEvent -> changeEvent.changedKeys().forEach(changeKey -> {
ConfigChange configChange = changeEvent.getChange(changeKey);
String newValue = configChange.getNewValue();
PropertyChangeType changeType = configChange.getChangeType();
switch (changeType) {
case ADDED:
case MODIFIED:
LOG.info("starting reload flow config... {} key={} value={},", changeType.name(), changeKey,
newValue);
LiteFlowChainELBuilder.createChain().setChainId(changeKey).setEL(newValue).build();
break;
case DELETED:
LOG.info("starting reload flow config... delete key={}", changeKey);
FlowBus.removeChain(changeKey);
break;
default:
}
}));
// chain if (StrUtil.isNotBlank(apolloParserConfigVO.getScriptNamespace())) {
chainConfig.addChangeListener(changeEvent -> changeEvent.changedKeys().forEach(changeKey -> { scriptConfig.addChangeListener(changeEvent -> changeEvent.changedKeys().forEach(changeKey -> {
ConfigChange configChange = changeEvent.getChange(changeKey); ConfigChange configChange = changeEvent.getChange(changeKey);
String newValue = configChange.getNewValue(); String newValue = configChange.getNewValue();
PropertyChangeType changeType = configChange.getChangeType();
switch (changeType) {
case ADDED:
case MODIFIED:
LOG.info("starting reload flow config... {} key={} value={},", changeType.name(), changeKey,
newValue);
LiteFlowChainELBuilder.createChain().setChainId(changeKey).setEL(newValue).build();
break;
case DELETED:
LOG.info("starting reload flow config... delete key={}", changeKey);
FlowBus.removeChain(changeKey);
} PropertyChangeType changeType = configChange.getChangeType();
})); if (DELETED.equals(changeType)) {
newValue = null;
}
NodeSimpleVO nodeSimpleVO = convert(changeKey, newValue);
if (Objects.isNull(nodeSimpleVO)) {
// key不符合规范的时候直接忽略
LOG.error("key={} is not a valid node config, ignore it", changeKey);
return;
}
switch (changeType) {
case ADDED:
case MODIFIED:
LOG.info("starting reload flow config... {} key={} value={},", changeType.name(), changeKey,
newValue);
if (StrUtil.isNotBlank(apolloParserConfigVO.getScriptNamespace())) { LiteFlowNodeBuilder.createScriptNode()
scriptConfig.addChangeListener(changeEvent -> changeEvent.changedKeys().forEach(changeKey -> { .setId(nodeSimpleVO.getNodeId())
ConfigChange configChange = changeEvent.getChange(changeKey); .setType(NodeTypeEnum.getEnumByCode(nodeSimpleVO.getType()))
String newValue = configChange.getNewValue(); .setName(nodeSimpleVO.getName())
.setScript(nodeSimpleVO.getScript())
.build();
break;
case DELETED:
LOG.info("starting reload flow config... delete key={}", changeKey);
FlowBus.getNodeMap().remove(nodeSimpleVO.getNodeId());
break;
default:
}
}));
}
}
PropertyChangeType changeType = configChange.getChangeType(); private NodeSimpleVO convert(String key, String value) {
// 不需要去理解这串正则,就是一个匹配冒号的
// 一定得是a:b或是a:b:c...这种完整类型的字符串的
List<String> matchItemList = ReUtil.findAllGroup0("(?<=[^:]:)[^:]+|[^:]+(?=:[^:])", key);
if (CollUtil.isEmpty(matchItemList)) {
return null;
}
NodeSimpleVO nodeSimpleVO; NodeSimpleVO nodeSimpleVO = new NodeSimpleVO();
switch (changeType) { if (matchItemList.size() > 1) {
case ADDED: nodeSimpleVO.setNodeId(matchItemList.get(0));
case MODIFIED: nodeSimpleVO.setType(matchItemList.get(1));
LOG.info("starting reload flow config... {} key={} value={},", changeType.name(), changeKey, }
newValue);
nodeSimpleVO = convert(changeKey, newValue);
LiteFlowNodeBuilder.createScriptNode()
.setId(nodeSimpleVO.getNodeId())
.setType(NodeTypeEnum.getEnumByCode(nodeSimpleVO.getType()))
.setName(nodeSimpleVO.getName())
.setScript(nodeSimpleVO.getScript())
.build();
break;
case DELETED:
LOG.info("starting reload flow config... delete key={}", changeKey);
nodeSimpleVO = convert(changeKey, null);
FlowBus.getNodeMap().remove(nodeSimpleVO.getNodeId());
}
}));
}
}
private NodeSimpleVO convert(String key, String value) { if (matchItemList.size() > 2) {
// 不需要去理解这串正则,就是一个匹配冒号的 nodeSimpleVO.setName(matchItemList.get(2));
// 一定得是a:b或是a:b:c...这种完整类型的字符串的 }
List<String> matchItemList = ReUtil.findAllGroup0("(?<=[^:]:)[^:]+|[^:]+(?=:[^:])", key);
if (CollUtil.isEmpty(matchItemList)) {
return null;
}
NodeSimpleVO nodeSimpleVO = new NodeSimpleVO(); // set script
if (matchItemList.size() > 1) { nodeSimpleVO.setScript(value);
nodeSimpleVO.setNodeId(matchItemList.get(0));
nodeSimpleVO.setType(matchItemList.get(1));
}
if (matchItemList.size() > 2) { return nodeSimpleVO;
nodeSimpleVO.setName(matchItemList.get(2)); }
}
// set script private static class NodeSimpleVO {
nodeSimpleVO.setScript(value);
return nodeSimpleVO; private String nodeId;
}
private static class NodeSimpleVO { private String type;
private String nodeId; private String name = StrUtil.EMPTY;
private String type; private String script;
private String name = StrUtil.EMPTY; public String getNodeId() {
return nodeId;
}
private String script; public void setNodeId(String nodeId) {
this.nodeId = nodeId;
}
public String getNodeId() { public String getType() {
return nodeId; return type;
} }
public void setNodeId(String nodeId) { public void setType(String type) {
this.nodeId = nodeId; this.type = type;
} }
public String getType() { public String getName() {
return type; return name;
} }
public void setType(String type) { public void setName(String name) {
this.type = type; this.name = name;
} }
public String getName() { public String getScript() {
return name; return script;
} }
public void setName(String name) { public void setScript(String script) {
this.name = name; this.script = script;
} }
public String getScript() { }
return script;
}
public void setScript(String script) {
this.script = script;
}
}
} }