This is an automated email from the ASF dual-hosted git repository. liubao pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/servicecomb-java-chassis.git
The following commit(s) were added to refs/heads/master by this push: new ca69cee [SCB-1711] support kie (#1518) ca69cee is described below commit ca69ceeec7a6eae824ad749dba15e8a6b9389ba3 Author: GuoYL <53255576+guoyl...@users.noreply.github.com> AuthorDate: Wed Feb 12 11:46:28 2020 +0800 [SCB-1711] support kie (#1518) * [SCB-1711] support kie (cherry picked from commit 833af5050ca3e26e75b327b499b0f9534f55d8b6) (cherry picked from commit cb19774f1ee1ac900c3d0be6c21b841a1758da0a) * [SCB-1711] optimization code --- coverage-reports/pom.xml | 4 + dependencies/default/pom.xml | 5 + distribution/pom.xml | 4 + .../config-kie}/pom.xml | 33 ++-- .../sources/KieConfigurationSourceImpl.java | 131 +++++++++++++ .../config/kie/client/ConnFailEvent.java | 35 ++++ .../config/kie/client/ConnSuccEvent.java | 22 +++ .../servicecomb/config/kie/client/KieClient.java | 165 +++++++++++++++++ .../servicecomb/config/kie/client/KieConfig.java | 98 ++++++++++ .../servicecomb/config/kie/client/KieUtil.java | 148 +++++++++++++++ .../servicecomb/config/kie/client/KieWatcher.java | 86 +++++++++ .../servicecomb/config/kie/model/KVBody.java | 65 +++++++ .../apache/servicecomb/config/kie/model/KVDoc.java | 118 ++++++++++++ .../servicecomb/config/kie/model/KVResponse.java | 73 ++++++++ .../config/kie/model/LabelDocResponse.java | 46 +++++ .../servicecomb/config/kie/model/ValueType.java | 37 ++++ ...comb.config.spi.ConfigCenterConfigurationSource | 18 ++ .../config/kie/client/TestKieClient.java | 205 +++++++++++++++++++++ .../config/kie/client/TestKieConfig.java | 46 +++++ .../config/kie/client/TestKieWatcher.java | 54 ++++++ .../kie/sources/TestKieConfigurationSource.java | 96 ++++++++++ .../src/test/resources/microservice.yaml | 56 ++++++ dynamic-config/pom.xml | 1 + handlers/handler-router/pom.xml | 1 + 24 files changed, 1528 insertions(+), 19 deletions(-) diff --git a/coverage-reports/pom.xml b/coverage-reports/pom.xml index 1103359..30a9100 100644 --- a/coverage-reports/pom.xml +++ b/coverage-reports/pom.xml @@ -184,6 +184,10 @@ </dependency> <dependency> <groupId>org.apache.servicecomb</groupId> + <artifactId>config-kie</artifactId> + </dependency> + <dependency> + <groupId>org.apache.servicecomb</groupId> <artifactId>edge-core</artifactId> </dependency> <dependency> diff --git a/dependencies/default/pom.xml b/dependencies/default/pom.xml index 15539c0..56c7903 100644 --- a/dependencies/default/pom.xml +++ b/dependencies/default/pom.xml @@ -1077,6 +1077,11 @@ </dependency> <dependency> <groupId>org.apache.servicecomb</groupId> + <artifactId>config-kie</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.servicecomb</groupId> <artifactId>deployment</artifactId> <version>${project.version}</version> </dependency> diff --git a/distribution/pom.xml b/distribution/pom.xml index 4f97aee..47afed4 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -61,6 +61,10 @@ <groupId>org.apache.servicecomb</groupId> <artifactId>config-cc</artifactId> </dependency> + <dependency> + <groupId>org.apache.servicecomb</groupId> + <artifactId>config-kie</artifactId> + </dependency> <!-- edge --> <dependency> diff --git a/handlers/handler-router/pom.xml b/dynamic-config/config-kie/pom.xml similarity index 69% copy from handlers/handler-router/pom.xml copy to dynamic-config/config-kie/pom.xml index f5b02d1..4aa67e5 100644 --- a/handlers/handler-router/pom.xml +++ b/dynamic-config/config-kie/pom.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> + <!-- ~ Licensed to the Apache Software Foundation (ASF) under one or more ~ contributor license agreements. See the NOTICE file distributed with @@ -20,42 +21,36 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> - <artifactId>handlers</artifactId> + <artifactId>dynamic-config</artifactId> <groupId>org.apache.servicecomb</groupId> <version>2.0.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> - <artifactId>handler-router</artifactId> + <artifactId>config-kie</artifactId> + <name>Java Chassis::Dynamic Config::Kie</name> <dependencies> <dependency> - <groupId>com.netflix.ribbon</groupId> - <artifactId>ribbon-loadbalancer</artifactId> - </dependency> - <dependency> - <groupId>org.springframework</groupId> - <artifactId>spring-core</artifactId> - </dependency> - <dependency> - <groupId>org.springframework</groupId> - <artifactId>spring-beans</artifactId> + <groupId>org.apache.servicecomb</groupId> + <artifactId>foundation-config</artifactId> </dependency> <dependency> - <groupId>com.google.guava</groupId> - <artifactId>guava</artifactId> + <groupId>org.apache.servicecomb</groupId> + <artifactId>deployment</artifactId> </dependency> <dependency> - <groupId>org.yaml</groupId> - <artifactId>snakeyaml</artifactId> + <groupId>org.apache.servicecomb</groupId> + <artifactId>foundation-ssl</artifactId> </dependency> <dependency> <groupId>org.apache.servicecomb</groupId> - <artifactId>common-rest</artifactId> + <artifactId>foundation-vertx</artifactId> </dependency> <dependency> - <groupId>org.apache.servicecomb</groupId> - <artifactId>handler-loadbalance</artifactId> + <groupId>io.vertx</groupId> + <artifactId>vertx-codegen</artifactId> + <scope>provided</scope> </dependency> </dependencies> </project> \ No newline at end of file diff --git a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/archaius/sources/KieConfigurationSourceImpl.java b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/archaius/sources/KieConfigurationSourceImpl.java new file mode 100644 index 0000000..dc9c1af --- /dev/null +++ b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/archaius/sources/KieConfigurationSourceImpl.java @@ -0,0 +1,131 @@ +/* + * 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 org.apache.servicecomb.config.kie.archaius.sources; + +import static com.netflix.config.WatchedUpdateResult.createIncremental; + +import com.google.common.collect.ImmutableMap; +import com.netflix.config.ConcurrentCompositeConfiguration; +import com.netflix.config.WatchedUpdateListener; +import com.netflix.config.WatchedUpdateResult; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import org.apache.commons.configuration.Configuration; +import org.apache.servicecomb.config.ConfigMapping; +import org.apache.servicecomb.config.kie.client.KieClient; +import org.apache.servicecomb.config.kie.client.KieConfig; +import org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class KieConfigurationSourceImpl implements ConfigCenterConfigurationSource { + + private static final Logger LOGGER = LoggerFactory.getLogger(KieConfigurationSourceImpl.class); + + private static final String KIE_CONFIG_URL_KEY = "servicecomb.kie.serverUri"; + + private final Map<String, Object> valueCache = new ConcurrentHashMap<>(); + + private List<WatchedUpdateListener> listeners = new CopyOnWriteArrayList<>(); + + private UpdateHandler updateHandler = new UpdateHandler(); + + private KieClient kieClient; + + @Override + public boolean isValidSource(Configuration localConfiguration) { + if (localConfiguration.getProperty(KIE_CONFIG_URL_KEY) == null) { + LOGGER.warn("Kie configuration source is not configured!"); + return false; + } + return true; + } + + @Override + public void init(Configuration localConfiguration) { + KieConfig.setFinalConfig((ConcurrentCompositeConfiguration) localConfiguration); + kieClient = new KieClient(updateHandler); + kieClient.refreshKieConfig(); + } + + @Override + public void destroy() { + if (kieClient == null) { + return; + } + kieClient.destroy(); + } + + @Override + public void addUpdateListener(WatchedUpdateListener watchedUpdateListener) { + listeners.add(watchedUpdateListener); + } + + @Override + public void removeUpdateListener(WatchedUpdateListener watchedUpdateListener) { + listeners.remove(watchedUpdateListener); + } + + @Override + public Map<String, Object> getCurrentData() throws Exception { + return valueCache; + } + + + private void updateConfiguration(WatchedUpdateResult result) { + for (WatchedUpdateListener l : listeners) { + try { + l.updateConfiguration(result); + } catch (Throwable ex) { + LOGGER.error("Error in invoking WatchedUpdateListener", ex); + } + } + } + + public class UpdateHandler { + + public void handle(String action, Map<String, Object> parseConfigs) { + if (parseConfigs == null || parseConfigs.isEmpty()) { + return; + } + Map<String, Object> configuration = ConfigMapping.getConvertedMap(parseConfigs); + if ("create".equals(action)) { + valueCache.putAll(configuration); + updateConfiguration(createIncremental(ImmutableMap.<String, Object>copyOf(configuration), + null, + null)); + } else if ("set".equals(action)) { + valueCache.putAll(configuration); + updateConfiguration( + createIncremental(null, ImmutableMap.<String, Object>copyOf(configuration), + null)); + } else if ("delete".equals(action)) { + configuration.keySet().forEach(valueCache::remove); + updateConfiguration(createIncremental(null, + null, + ImmutableMap.<String, Object>copyOf(configuration))); + } else { + LOGGER.error("action: {} is invalid.", action); + return; + } + LOGGER.warn("Config value cache changed: action:{}; item:{}", action, configuration.keySet()); + } + } +} diff --git a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/client/ConnFailEvent.java b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/client/ConnFailEvent.java new file mode 100644 index 0000000..1e34810 --- /dev/null +++ b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/client/ConnFailEvent.java @@ -0,0 +1,35 @@ +/* + * 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 org.apache.servicecomb.config.kie.client; + +public class ConnFailEvent { + + private String msg; + + public ConnFailEvent(String msg) { + this.msg = msg; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } +} diff --git a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/client/ConnSuccEvent.java b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/client/ConnSuccEvent.java new file mode 100644 index 0000000..95d9758 --- /dev/null +++ b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/client/ConnSuccEvent.java @@ -0,0 +1,22 @@ +/* + * 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 org.apache.servicecomb.config.kie.client; + +public class ConnSuccEvent { + +} diff --git a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/client/KieClient.java b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/client/KieClient.java new file mode 100644 index 0000000..c606c2c --- /dev/null +++ b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/client/KieClient.java @@ -0,0 +1,165 @@ +/* + * 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 org.apache.servicecomb.config.kie.client; + +import io.netty.handler.codec.http.HttpResponseStatus; +import io.vertx.core.DeploymentOptions; +import io.vertx.core.Vertx; +import io.vertx.core.VertxOptions; +import io.vertx.core.http.HttpClientOptions; +import io.vertx.core.http.HttpClientRequest; +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import org.apache.http.HttpStatus; +import org.apache.servicecomb.config.kie.archaius.sources.KieConfigurationSourceImpl.UpdateHandler; +import org.apache.servicecomb.config.kie.model.KVResponse; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.apache.servicecomb.foundation.common.net.IpPort; +import org.apache.servicecomb.foundation.common.net.NetUtils; +import org.apache.servicecomb.foundation.common.utils.JsonUtils; +import org.apache.servicecomb.foundation.vertx.AddressResolverConfig; +import org.apache.servicecomb.foundation.vertx.VertxUtils; +import org.apache.servicecomb.foundation.vertx.client.ClientPoolManager; +import org.apache.servicecomb.foundation.vertx.client.ClientVerticle; +import org.apache.servicecomb.foundation.vertx.client.http.HttpClientPoolFactory; +import org.apache.servicecomb.foundation.vertx.client.http.HttpClientWithContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class KieClient { + + private static final Logger LOGGER = LoggerFactory.getLogger(KieClient.class); + + private ScheduledExecutorService EXECUTOR = Executors.newScheduledThreadPool(1); + + private static final long TIME_OUT = 10000; + + private static final KieConfig KIE_CONFIG = KieConfig.INSTANCE; + + private final int refreshInterval = KIE_CONFIG.getRefreshInterval(); + + private final int firstRefreshInterval = KIE_CONFIG.getFirstRefreshInterval(); + + private final String serviceUri = KIE_CONFIG.getServerUri(); + + private ClientPoolManager<HttpClientWithContext> clientMgr; + + public KieClient(UpdateHandler updateHandler) { + KieWatcher.INSTANCE.setUpdateHandler(updateHandler); + } + + public void refreshKieConfig() { + try { + deployConfigClient(); + } catch (InterruptedException e) { + throw new IllegalStateException(e); + } + EXECUTOR + .scheduleWithFixedDelay(new ConfigRefresh(serviceUri), firstRefreshInterval, + refreshInterval, TimeUnit.SECONDS); + } + + private void deployConfigClient() throws InterruptedException { + VertxOptions vertxOptions = new VertxOptions(); + vertxOptions.setAddressResolverOptions(AddressResolverConfig.getAddressResover("kie.consumer", + KieConfig.getFinalConfig())); + Vertx vertx = VertxUtils.getOrCreateVertxByName("kie", vertxOptions); + + HttpClientOptions httpClientOptions = new HttpClientOptions(); + clientMgr = new ClientPoolManager<>(vertx, new HttpClientPoolFactory(httpClientOptions)); + + DeploymentOptions deployOptions = VertxUtils.createClientDeployOptions(clientMgr, 1); + VertxUtils.blockDeploy(vertx, ClientVerticle.class, deployOptions); + } + + public void destroy() { + if (EXECUTOR != null) { + EXECUTOR.shutdown(); + EXECUTOR = null; + } + } + + class ConfigRefresh implements Runnable { + + private final String serviceUri; + + ConfigRefresh(String serviceUris) { + this.serviceUri = serviceUris; + } + + @Override + public void run() { + try { + refreshConfig(); + } catch (Throwable e) { + LOGGER.error("client refresh thread exception ", e); + } + } + + //todo : latch down + @SuppressWarnings("deprecation") + void refreshConfig() { + String path = "/v1/" + + KieConfig.INSTANCE.getDomainName() + + "/kie/kv?label=app:" + + KieConfig.INSTANCE.getAppName(); + clientMgr.findThreadBindClientPool().runOnContext(client -> { + IpPort ipPort = NetUtils.parseIpPortFromURI(serviceUri); + HttpClientRequest request = client + .get(ipPort.getPort(), ipPort.getHostOrIp(), path, rsp -> { + if (rsp.statusCode() == HttpResponseStatus.OK.code()) { + rsp.bodyHandler(buf -> { + try { + Map<String, Object> resMap = KieUtil.getConfigByLabel(JsonUtils.OBJ_MAPPER + .readValue(buf.toString(), KVResponse.class)); + KieWatcher.INSTANCE.refreshConfigItems(resMap); + EventManager.post(new ConnSuccEvent()); + } catch (IOException e) { + EventManager.post(new ConnFailEvent( + "config update result parse fail " + e.getMessage())); + LOGGER.error("Config update from {} failed. Error message is [{}].", + serviceUri, + e.getMessage()); + } + }); + // latch.countDown(); + } else if (rsp.statusCode() == HttpStatus.SC_NOT_FOUND) { + EventManager.post(new ConnSuccEvent()); +// latch.countDown(); + } else { + EventManager.post(new ConnFailEvent("fetch config fail")); + LOGGER.error("Config update from {} failed. Error message is [{}].", + serviceUri, + rsp.statusMessage()); + } + }).setTimeout(TIME_OUT); + request.exceptionHandler(e -> { + EventManager.post(new ConnFailEvent("fetch config fail")); + LOGGER.error("Config update from {} failed. Error message is [{}].", + serviceUri, + e.getMessage()); +// latch.countDown(); + }); + request.end(); + }); + } + } +} diff --git a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/client/KieConfig.java b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/client/KieConfig.java new file mode 100644 index 0000000..a0b45e2 --- /dev/null +++ b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/client/KieConfig.java @@ -0,0 +1,98 @@ +/* + * 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 org.apache.servicecomb.config.kie.client; + +import static org.apache.servicecomb.foundation.common.base.ServiceCombConstants.DEFAULT_SERVICECOMB_ENV; +import static org.apache.servicecomb.foundation.common.base.ServiceCombConstants.SERVICECOMB_ENV; + +import com.netflix.config.ConcurrentCompositeConfiguration; + +public class KieConfig { + + public static final KieConfig INSTANCE = new KieConfig(); + + private static ConcurrentCompositeConfiguration finalConfig; + + private static final String SERVER_URL_KEY = "servicecomb.kie.serverUri"; + + private static final String REFRESH_INTERVAL = "servicecomb.kie.refreshInterval"; + + private static final String FIRST_REFRESH_INTERVAL = "servicecomb.kie.firstRefreshInterval"; + + private static final String DOMAIN_NAME = "servicecomb.kie.domainName"; + + private static final String APPLICATION_NAME = "APPLICATION_ID"; + + private static final String SERVICE_NAME = "service_description.name"; + + private static final String SERVICE_VERSION = "service_description.version"; + + private static final String INSTANCE_TAGS = "instance_description.properties.tags"; + + + private static final int DEFAULT_REFRESH_INTERVAL = 3000; + + private static final int DEFAULT_FIRST_REFRESH_INTERVAL = 0; + + private KieConfig() { + } + + public static ConcurrentCompositeConfiguration getFinalConfig() { + return finalConfig; + } + + public static void setFinalConfig(ConcurrentCompositeConfiguration finalConfig) { + KieConfig.finalConfig = finalConfig; + } + + public String getVersion() { + return finalConfig.getString(SERVICE_VERSION); + } + + public String getServiceName() { + return finalConfig.getString(SERVICE_NAME); + } + + public String getTags() { + return finalConfig.getString(INSTANCE_TAGS); + } + + public String getEnvironment() { + return finalConfig.getString(SERVICECOMB_ENV, DEFAULT_SERVICECOMB_ENV); + } + + public String getAppName() { + return finalConfig.getString(APPLICATION_NAME); + } + + public String getDomainName() { + return finalConfig.getString(DOMAIN_NAME, "default"); + } + + public String getServerUri() { + return finalConfig.getString(SERVER_URL_KEY); + } + + public int getRefreshInterval() { + return finalConfig.getInt(REFRESH_INTERVAL, DEFAULT_REFRESH_INTERVAL); + } + + public int getFirstRefreshInterval() { + return finalConfig.getInt(FIRST_REFRESH_INTERVAL, DEFAULT_FIRST_REFRESH_INTERVAL); + } +} diff --git a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/client/KieUtil.java b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/client/KieUtil.java new file mode 100644 index 0000000..1e91d37 --- /dev/null +++ b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/client/KieUtil.java @@ -0,0 +1,148 @@ +/* + * 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 org.apache.servicecomb.config.kie.client; + +import java.io.StringReader; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import org.apache.servicecomb.config.kie.model.KVDoc; +import org.apache.servicecomb.config.kie.model.KVResponse; +import org.apache.servicecomb.config.kie.model.ValueType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.util.StringUtils; + +public class KieUtil { + private static final Logger LOGGER = LoggerFactory.getLogger(KieUtil.class); + + public static String encrypt(String dataStr) { + MessageDigest messageDigest = null; + String result = ""; + try { + messageDigest = MessageDigest.getInstance("MD5"); + messageDigest.update(dataStr.getBytes(StandardCharsets.UTF_8)); + result = new BigInteger(1, messageDigest.digest(dataStr.getBytes(StandardCharsets.UTF_8))) + .toString(16); + } catch (NoSuchAlgorithmException e) { + LOGGER.error("Failed to generate MD5 . ", e); + } + return result; + } + + public static Map<String, Object> getConfigByLabel(KVResponse resp) { + Map<String, Object> resultMap = new HashMap<>(); + List<KVDoc> appList = new ArrayList<>(); + List<KVDoc> serviceList = new ArrayList<>(); + List<KVDoc> versionList = new ArrayList<>(); + for (KVDoc kvDoc : resp.getData()) { + Map<String, String> labelsMap = kvDoc.getLabels(); + //todo:how to deal env + if (labelsMap.containsKey("app") && labelsMap.get("app") + .equals(KieConfig.INSTANCE.getAppName()) + && labelsMap.containsKey("env") && labelsMap.get("env") + .equals(KieConfig.INSTANCE.getEnvironment())) { + if (!labelsMap.containsKey("service")) { + appList.add(kvDoc); + } + if (labelsMap.containsKey("service") && labelsMap.get("service") + .equals(KieConfig.INSTANCE.getServiceName())) { + if (!kvDoc.getLabels().containsKey("version")) { + serviceList.add(kvDoc); + } + if (labelsMap.containsKey("version") && labelsMap.get("version") + .equals(KieConfig.INSTANCE.getServiceName())) { + versionList.add(kvDoc); + } + } + } + } + //kv is priority + for (KVDoc kvDoc : appList) { + resultMap.putAll(processValueType(kvDoc)); + } + for (KVDoc kvDoc : serviceList) { + resultMap.putAll(processValueType(kvDoc)); + } + for (KVDoc kvDoc : versionList) { + resultMap.putAll(processValueType(kvDoc)); + } + return resultMap; + } + + public static Map<String, String> processValueType(KVDoc kvDoc) { + ValueType vtype; + try { + vtype = ValueType.valueOf(kvDoc.getValueType()); + } catch (IllegalArgumentException e) { + throw new RuntimeException("value type not support"); + } + Properties properties = new Properties(); + Map<String, String> kvMap = new HashMap<>(); + try { + if (vtype == (ValueType.YMAL) || vtype == (ValueType.YML)) { + YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean(); + yamlFactory.setResources(new ByteArrayResource(kvDoc.getValue().getBytes())); + properties = yamlFactory.getObject(); + } else if (vtype == (ValueType.PROPERTIES)) { + properties.load(new StringReader(kvDoc.getValue())); + } else if (vtype == (ValueType.TEXT) || vtype == (ValueType.STRING)) { + kvMap.put(kvDoc.getKey(), kvDoc.getValue()); + return kvMap; + } else { + kvMap.put(kvDoc.getKey(), kvDoc.getValue()); + return kvMap; + } + kvMap = toMap(kvDoc.getKey(), properties); + return kvMap; + } catch (Exception e) { + LOGGER.error("read config failed"); + } + return Collections.emptyMap(); + } + + + @SuppressWarnings("unchecked") + private static Map<String, String> toMap(String prefix, Properties properties) { + Map<String, String> result = new HashMap<>(); + Enumeration<String> keys = (Enumeration<String>) properties.propertyNames(); + while (keys.hasMoreElements()) { + String key = keys.nextElement(); + if (!StringUtils.isEmpty(prefix)) { + key = prefix + "." + key; + } + Object value = properties.getProperty(key); + if (value != null) { + result.put(key, ((String) value).trim()); + } else { + result.put(key, null); + } + } + return result; + } +} diff --git a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/client/KieWatcher.java b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/client/KieWatcher.java new file mode 100644 index 0000000..4ea5e26 --- /dev/null +++ b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/client/KieWatcher.java @@ -0,0 +1,86 @@ +/* + * 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 org.apache.servicecomb.config.kie.client; + +import java.util.HashMap; +import java.util.Map; +import org.apache.servicecomb.config.kie.archaius.sources.KieConfigurationSourceImpl.UpdateHandler; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +public class KieWatcher { + + public static final KieWatcher INSTANCE = new KieWatcher(); + + private UpdateHandler updateHandler; + + private String refreshRecord; + + Map<String, Object> lastTimeData; + + private KieWatcher() { + } + + public void setUpdateHandler(UpdateHandler updateHandler) { + this.updateHandler = updateHandler; + } + + public void refreshConfigItems(Map<String, Object> remoteItems) { + String md5Vaule = KieUtil.encrypt(remoteItems.toString()); + if (CollectionUtils.isEmpty(remoteItems)) { + updateHandler.handle("delete", remoteItems); + lastTimeData = remoteItems; + return; + } + if (StringUtils.isEmpty(refreshRecord)) { + refreshRecord = md5Vaule; + updateHandler.handle("create", remoteItems); + lastTimeData = remoteItems; + return; + } + if (md5Vaule.equals(refreshRecord)) { + return; + } + refreshRecord = md5Vaule; + doRefresh(remoteItems); + lastTimeData = remoteItems; + } + + + private void doRefresh(Map<String, Object> remoteItems) { + Map<String, Object> itemsCreated = new HashMap<>(); + Map<String, Object> itemsDeleted = new HashMap<>(); + Map<String, Object> itemsModified = new HashMap<>(); + for (String itemKey : remoteItems.keySet()) { + if (!lastTimeData.containsKey(itemKey)) { + itemsCreated.put(itemKey, remoteItems.get(itemKey)); + } else if (!remoteItems.get(itemKey).equals(lastTimeData.get(itemKey))) { + itemsModified.put(itemKey, remoteItems.get(itemKey)); + } + } + for (String itemKey : lastTimeData.keySet()) { + if (!remoteItems.containsKey(itemKey)) { + itemsDeleted.put(itemKey, ""); + } + } + updateHandler.handle("create", itemsCreated); + updateHandler.handle("set", itemsModified); + updateHandler.handle("delete", itemsDeleted); + } + +} diff --git a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/model/KVBody.java b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/model/KVBody.java new file mode 100644 index 0000000..b7deee0 --- /dev/null +++ b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/model/KVBody.java @@ -0,0 +1,65 @@ +/* + * 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 org.apache.servicecomb.config.kie.model; + +import com.fasterxml.jackson.annotation.JsonAlias; +import java.util.HashMap; +import java.util.Map; +import org.apache.servicecomb.config.kie.client.KieConfig; + +public class KVBody { + + private Map<String, String> labels = new HashMap<String, String>(); + + private String value; + + @JsonAlias("value_type") + private String valueType; + + public Map<String, String> getLabels() { + return labels; + } + + public void setLabels(Map<String, String> labels) { + this.labels = labels; + } + + public void initLabels() { + labels.put("env", KieConfig.INSTANCE.getEnvironment()); + labels.put("app", KieConfig.INSTANCE.getAppName()); + labels.put("service", KieConfig.INSTANCE.getServiceName()); + labels.put("version", KieConfig.INSTANCE.getVersion()); + labels.put("tags", KieConfig.INSTANCE.getTags()); + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getValueType() { + return valueType; + } + + public void setValueType(String valueType) { + this.valueType = valueType; + } +} diff --git a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/model/KVDoc.java b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/model/KVDoc.java new file mode 100644 index 0000000..73c02e8 --- /dev/null +++ b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/model/KVDoc.java @@ -0,0 +1,118 @@ +/* + * 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 org.apache.servicecomb.config.kie.model; + +import com.fasterxml.jackson.annotation.JsonAlias; +import java.util.HashMap; +import java.util.Map; + +public class KVDoc { + + @JsonAlias("_id") + private String id; + + private String check; + + private String domain; + + private String key; + + @JsonAlias("label_id") + private String labelId; + + private Map<String, String> labels = new HashMap<String, String>(); + + private Integer revision; + + private String value; + + @JsonAlias("value_type") + private String valueType; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getCheck() { + return check; + } + + public String getDomain() { + return domain; + } + + public String getLabelId() { + return labelId; + } + + public Map<String, String> getLabels() { + return labels; + } + + public Integer getRevision() { + return revision; + } + + public String getValue() { + return value; + } + + public void setCheck(String check) { + this.check = check; + } + + public void setDomain(String domain) { + this.domain = domain; + } + + public void setLabelId(String labelId) { + this.labelId = labelId; + } + + public void setLabels(Map<String, String> labels) { + this.labels = labels; + } + + public void setRevision(Integer revision) { + this.revision = revision; + } + + public void setValueType(String valueType) { + this.valueType = valueType; + } + + public void setValue(String value) { + this.value = value; + } + + public String getValueType() { + return valueType; + } +} diff --git a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/model/KVResponse.java b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/model/KVResponse.java new file mode 100644 index 0000000..79bc6aa --- /dev/null +++ b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/model/KVResponse.java @@ -0,0 +1,73 @@ +/* + * 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 org.apache.servicecomb.config.kie.model; + +import java.util.List; + +public class KVResponse { + + private List<KVDoc> data; + + private LabelDocResponse label; + + private Integer num; + + private Integer size; + + private Integer total; + + public Integer getNum() { + return num; + } + + public void setNum(Integer num) { + this.num = num; + } + + public Integer getSize() { + return size; + } + + public void setSize(Integer size) { + this.size = size; + } + + public Integer getTotal() { + return total; + } + + public void setTotal(Integer total) { + this.total = total; + } + + public List<KVDoc> getData() { + return data; + } + + public LabelDocResponse getLabel() { + return label; + } + + public void setData(List<KVDoc> data) { + this.data = data; + } + + public void setLabel(LabelDocResponse label) { + this.label = label; + } +} diff --git a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/model/LabelDocResponse.java b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/model/LabelDocResponse.java new file mode 100644 index 0000000..fb2b541 --- /dev/null +++ b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/model/LabelDocResponse.java @@ -0,0 +1,46 @@ +/* + * 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 org.apache.servicecomb.config.kie.model; + +import com.fasterxml.jackson.annotation.JsonAlias; +import java.util.HashMap; +import java.util.Map; + +public class LabelDocResponse { + + @JsonAlias("label_id") + private String labelId; + + private Map<String, String> labels = new HashMap<String, String>(); + + public String getLabelId() { + return labelId; + } + + public Map<String, String> getLabels() { + return labels; + } + + public void setLabelId(String labelId) { + this.labelId = labelId; + } + + public void setLabels(Map<String, String> labels) { + this.labels = labels; + } +} diff --git a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/model/ValueType.java b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/model/ValueType.java new file mode 100644 index 0000000..0658be2 --- /dev/null +++ b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/model/ValueType.java @@ -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 org.apache.servicecomb.config.kie.model; + +public enum ValueType { + YML("yml"), + YMAL("yaml"), + STRING("string"), + TEXT("text"), + JSON("json"), + PROPERTIES("properties"); + + private String value; + + ValueType(String value) { + this.value = value; + } + + public String getValue() { + return this.value; + } +} diff --git a/dynamic-config/config-kie/src/main/resources/META-INF/services/org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource b/dynamic-config/config-kie/src/main/resources/META-INF/services/org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource new file mode 100644 index 0000000..c696fa9 --- /dev/null +++ b/dynamic-config/config-kie/src/main/resources/META-INF/services/org.apache.servicecomb.config.spi.ConfigCenterConfigurationSource @@ -0,0 +1,18 @@ +# +# 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. +# + +org.apache.servicecomb.config.kie.archaius.sources.KieConfigurationSourceImpl \ No newline at end of file diff --git a/dynamic-config/config-kie/src/test/java/org/apache/servicecomb/config/kie/client/TestKieClient.java b/dynamic-config/config-kie/src/test/java/org/apache/servicecomb/config/kie/client/TestKieClient.java new file mode 100644 index 0000000..2bcd0f1 --- /dev/null +++ b/dynamic-config/config-kie/src/test/java/org/apache/servicecomb/config/kie/client/TestKieClient.java @@ -0,0 +1,205 @@ +/* + * 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 org.apache.servicecomb.config.kie.client; + +import com.google.common.eventbus.Subscribe; +import io.vertx.core.Handler; +import io.vertx.core.MultiMap; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpClient; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.HttpClientResponse; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.json.JsonObject; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ScheduledExecutorService; +import mockit.Deencapsulation; +import mockit.Expectations; +import mockit.Mock; +import mockit.MockUp; +import mockit.Mocked; +import org.apache.servicecomb.config.ConfigUtil; +import org.apache.servicecomb.config.kie.archaius.sources.KieConfigurationSourceImpl; +import org.apache.servicecomb.config.kie.archaius.sources.KieConfigurationSourceImpl.UpdateHandler; +import org.apache.servicecomb.config.kie.client.KieClient.ConfigRefresh; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.apache.servicecomb.foundation.vertx.client.ClientPoolManager; +import org.apache.servicecomb.foundation.vertx.client.http.HttpClientWithContext; +import org.apache.servicecomb.foundation.vertx.client.http.HttpClientWithContext.RunHandler; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.Mockito; + +@SuppressWarnings("deprecation") +public class TestKieClient { + + String mockKvResponse = "{\n" + + " \"data\": [\n" + + " {\n" + + " \"_id\": \"string\",\n" + + " \"check\": \"string\",\n" + + " \"domain\": \"string\",\n" + + " \"key\": \"string\",\n" + + " \"label_id\": \"string\",\n" + + " \"labels\": {\n" + + " \"additionalProp1\": \"string\",\n" + + " \"additionalProp2\": \"string\",\n" + + " \"additionalProp3\": \"string\"\n" + + " },\n" + + " \"project\": \"string\",\n" + + " \"revision\": 0,\n" + + " \"value\": \"string\",\n" + + " \"value_type\": \"string\"\n" + + " }\n" + + " ],\n" + + " \"label\": {\n" + + " \"label_id\": \"string\",\n" + + " \"labels\": {\n" + + " \"additionalProp1\": \"string\",\n" + + " \"additionalProp2\": \"string\",\n" + + " \"additionalProp3\": \"string\"\n" + + " }\n" + + " },\n" + + " \"num\": 0,\n" + + " \"size\": 0,\n" + + " \"total\": 0\n" + + "}"; + + @BeforeClass + public static void setUpClass() { + KieConfig.setFinalConfig(ConfigUtil.createLocalConfig()); + } + + @SuppressWarnings("unchecked") + @Test + public void testRefreshKieConfig() { + HttpClientRequest request = Mockito.mock(HttpClientRequest.class); + Mockito.when(request.method()).thenReturn(HttpMethod.GET); + Mockito.when(request.headers()).thenReturn(MultiMap.caseInsensitiveMultiMap()); + Buffer rsp = Mockito.mock(Buffer.class); + Mockito.when(rsp.toJsonObject()).thenReturn(new JsonObject(mockKvResponse)); + HttpClientResponse event = Mockito.mock(HttpClientResponse.class); + Mockito.when(event.bodyHandler(Mockito.any(Handler.class))).then(invocation -> { + Handler<Buffer> handler = invocation.getArgumentAt(0, Handler.class); + handler.handle(rsp); + return null; + }); + Mockito.when(event.statusCode()).thenReturn(200); + HttpClient httpClient = Mockito.mock(HttpClient.class); + Mockito.when( + httpClient.get(Mockito.anyInt(), Mockito.anyString(), Mockito.anyString(), + Mockito.any(Handler.class))) + .then(invocation -> { + Handler<HttpClientResponse> handler = invocation.getArgumentAt(3, Handler.class); + handler.handle(event); + return request; + }); + new MockUp<HttpClientWithContext>() { + @Mock + public void runOnContext(RunHandler handler) { + handler.run(httpClient); + } + }; + UpdateHandler updateHandler = new KieConfigurationSourceImpl().new UpdateHandler(); + KieClient kie = new KieClient(updateHandler); + kie.refreshKieConfig(); + } + + + public static class ConfigRefreshExceptionEvent { + Map<String, String> map; + + public ConfigRefreshExceptionEvent(Map<String, String> map) { + this.map = map; + } + + @Subscribe + public void testMsg(Object event) { + if (event instanceof ConnFailEvent) { + map.put("result", "Fail event trigger"); + } + if (event instanceof ConnSuccEvent) { + map.put("result", "Succ event trigger"); + } + } + } + + @SuppressWarnings("unchecked") + @Test + public void testConfigRefreshException(@Mocked ClientPoolManager<HttpClientWithContext> clientMgr, + @Mocked HttpClientWithContext httpClientWithContext) { + KieConfigurationSourceImpl impl = new KieConfigurationSourceImpl(); + Map<String, String> map = new HashMap<>(); + EventManager.register(new ConfigRefreshExceptionEvent(map)); + UpdateHandler updateHandler = impl.new UpdateHandler(); + HttpClientRequest request = Mockito.mock(HttpClientRequest.class); + Mockito.when(request.headers()).thenReturn(MultiMap.caseInsensitiveMultiMap()); + Buffer rsp = Mockito.mock(Buffer.class); + Mockito.when(rsp.toString()).thenReturn(mockKvResponse); + + HttpClientResponse event = Mockito.mock(HttpClientResponse.class); + Mockito.when(event.bodyHandler(Mockito.any(Handler.class))).then(invocation -> { + Handler<Buffer> handler = invocation.getArgumentAt(0, Handler.class); + handler.handle(rsp); + return null; + }); + Mockito.when(event.statusCode()).thenReturn(400); + Buffer buf = Mockito.mock(Buffer.class); + Mockito.when(buf.toJsonObject()).thenReturn(new JsonObject(mockKvResponse)); + HttpClient httpClient = Mockito.mock(HttpClient.class); + Mockito.when( + httpClient.get(Mockito.anyInt(), Mockito.anyString(), Mockito.anyString(), + Mockito.any(Handler.class))) + .then(invocation -> { + Handler<HttpClientResponse> handler = invocation.getArgumentAt(3, Handler.class); + handler.handle(event); + return request; + }); + new MockUp<HttpClientWithContext>() { + @Mock + public void runOnContext(RunHandler handler) { + handler.run(httpClient); + } + }; + new Expectations() { + { + clientMgr.findThreadBindClientPool(); + result = httpClientWithContext; + } + }; + KieClient kie = new KieClient(updateHandler); + Deencapsulation.setField(kie, "clientMgr", clientMgr); + ConfigRefresh refresh = kie.new ConfigRefresh("http://configcentertest:30103"); + refresh.run(); + Assert.assertEquals("Fail event trigger", map.get("result")); + Mockito.when(event.statusCode()).thenReturn(200); + refresh.run(); + Assert.assertEquals("Succ event trigger", map.get("result")); + } + + @Test + public void destroy() { + KieClient kieClient = new KieClient(null); + ScheduledExecutorService executor = Deencapsulation.getField(kieClient, "EXECUTOR"); + Assert.assertFalse(executor.isShutdown()); + executor.shutdown(); + Assert.assertTrue(executor.isShutdown()); + } +} diff --git a/dynamic-config/config-kie/src/test/java/org/apache/servicecomb/config/kie/client/TestKieConfig.java b/dynamic-config/config-kie/src/test/java/org/apache/servicecomb/config/kie/client/TestKieConfig.java new file mode 100644 index 0000000..d0f0676 --- /dev/null +++ b/dynamic-config/config-kie/src/test/java/org/apache/servicecomb/config/kie/client/TestKieConfig.java @@ -0,0 +1,46 @@ +/* + * 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 org.apache.servicecomb.config.kie.client; + +import org.apache.servicecomb.config.ConfigUtil; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class TestKieConfig { + + @BeforeClass + public static void setUpClass() { + KieConfig.setFinalConfig(ConfigUtil.createLocalConfig()); + } + + @Test + public void getServerUri() { + String servers = KieConfig.INSTANCE.getServerUri(); + Assert.assertEquals("https://172.16.8.7:30103", servers); + } + + @Test + public void getEnvironment() { + Assert.assertEquals("testing", KieConfig.INSTANCE.getEnvironment()); + System.setProperty("SERVICECOMB_ENV", "development"); + KieConfig.setFinalConfig(ConfigUtil.createLocalConfig()); + Assert.assertEquals("development", KieConfig.INSTANCE.getEnvironment()); + } + +} diff --git a/dynamic-config/config-kie/src/test/java/org/apache/servicecomb/config/kie/client/TestKieWatcher.java b/dynamic-config/config-kie/src/test/java/org/apache/servicecomb/config/kie/client/TestKieWatcher.java new file mode 100644 index 0000000..126107e --- /dev/null +++ b/dynamic-config/config-kie/src/test/java/org/apache/servicecomb/config/kie/client/TestKieWatcher.java @@ -0,0 +1,54 @@ +/* + * 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 org.apache.servicecomb.config.kie.client; + +import java.util.HashMap; +import java.util.Map; +import mockit.Deencapsulation; +import org.apache.servicecomb.config.kie.archaius.sources.KieConfigurationSourceImpl; +import org.apache.servicecomb.config.kie.archaius.sources.KieConfigurationSourceImpl.UpdateHandler; +import org.junit.Assert; +import org.junit.Test; + +public class TestKieWatcher { + + private KieConfigurationSourceImpl kieSource = new KieConfigurationSourceImpl(); + + private UpdateHandler uh = kieSource.new UpdateHandler(); + + { + KieWatcher.INSTANCE.setUpdateHandler(uh); + } + + @Test + public void testRefreshConfigItems() { + boolean status = true; + Map<String, Object> configMap = new HashMap<>(); + configMap.put("key1", "application1"); + configMap.put("key2", "application2"); + configMap.put("key3", "application3"); + configMap.put("key4", "application4"); + Map<String, Object> result = null; + try { + result = Deencapsulation.invoke(KieWatcher.INSTANCE, "refreshConfigItems", configMap); + } catch (Exception e) { + status = false; + } + Assert.assertTrue(status); + } +} diff --git a/dynamic-config/config-kie/src/test/java/org/apache/servicecomb/config/kie/sources/TestKieConfigurationSource.java b/dynamic-config/config-kie/src/test/java/org/apache/servicecomb/config/kie/sources/TestKieConfigurationSource.java new file mode 100644 index 0000000..73cfae5 --- /dev/null +++ b/dynamic-config/config-kie/src/test/java/org/apache/servicecomb/config/kie/sources/TestKieConfigurationSource.java @@ -0,0 +1,96 @@ +/* + * 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 org.apache.servicecomb.config.kie.sources; + +import com.netflix.config.WatchedUpdateListener; +import com.netflix.config.WatchedUpdateResult; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import mockit.Deencapsulation; +import mockit.Mock; +import mockit.MockUp; +import org.apache.commons.lang.reflect.FieldUtils; +import org.apache.servicecomb.config.kie.archaius.sources.KieConfigurationSourceImpl; +import org.apache.servicecomb.config.kie.archaius.sources.KieConfigurationSourceImpl.UpdateHandler; +import org.apache.servicecomb.config.kie.client.KieClient; +import org.junit.Assert; +import org.junit.Test; + +public class TestKieConfigurationSource { + + @Test + public void testCreate() { + + KieConfigurationSourceImpl kieSource = new KieConfigurationSourceImpl(); + kieSource.addUpdateListener(result -> Assert.assertTrue(!result.getAdded().isEmpty())); + UpdateHandler udateHandler = Deencapsulation.getField(kieSource, UpdateHandler.class); + Map<String, Object> addedItems = new HashMap<>(); + addedItems.put("testKey", "testValue"); + udateHandler.handle("create", addedItems); + } + + @Test + public void testUpdate() { + + KieConfigurationSourceImpl kieSource = new KieConfigurationSourceImpl(); + kieSource.addUpdateListener(result -> Assert.assertTrue(!result.getChanged().isEmpty())); + UpdateHandler udateHandler = Deencapsulation.getField(kieSource, UpdateHandler.class); + Map<String, Object> addedItems = new HashMap<>(); + addedItems.put("testKey", "testValue"); + udateHandler.handle("set", addedItems); + } + + @Test + public void testDelete() throws Exception { + KieConfigurationSourceImpl kieSource = new KieConfigurationSourceImpl(); + kieSource.addUpdateListener(result -> Assert.assertTrue(!result.getDeleted().isEmpty())); + UpdateHandler udateHandler = Deencapsulation.getField(kieSource, UpdateHandler.class); + Map<String, Object> addedItems = new HashMap<>(); + addedItems.put("testKey", "testValue"); + + kieSource.getCurrentData().put("testKey", "testValue"); + udateHandler.handle("delete", addedItems); + Assert.assertTrue(kieSource.getCurrentData().isEmpty()); + } + + @Test + public void destroy_notInit() { + KieConfigurationSourceImpl kieSource = new KieConfigurationSourceImpl(); + + // need not throw exception + kieSource.destroy(); + } + + @Test + public void destroy_inited() throws IllegalAccessException { + AtomicInteger count = new AtomicInteger(); + KieClient kieClient = new MockUp<KieClient>() { + @Mock + void destroy() { + count.incrementAndGet(); + } + }.getMockInstance(); + KieConfigurationSourceImpl kieSource = new KieConfigurationSourceImpl(); + FieldUtils + .writeDeclaredField(kieSource, "kieClient", kieClient, true); + + kieSource.destroy(); + + Assert.assertEquals(1, count.get()); + } +} diff --git a/dynamic-config/config-kie/src/test/resources/microservice.yaml b/dynamic-config/config-kie/src/test/resources/microservice.yaml new file mode 100644 index 0000000..43c4863 --- /dev/null +++ b/dynamic-config/config-kie/src/test/resources/microservice.yaml @@ -0,0 +1,56 @@ +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- + +host.name: 172.16.8.7 +trace: + handler: + enabled: false + sampler: + percent: 0.5 + metric: + service: + enable: false +validate: + parameter: + enabled: true + returnValue: + enabled: true + apiInvoke: + enabled: true +shutDownHandler: + enabled: true + timeLimit: 30000 +eureka: + instance: + preferIpAddress: true + leaseRenewalIntervalInSeconds: 3 + leaseExpirationDurationInSeconds: 5 + client: + serviceUrl: + defaultZone: http://172.16.8.8:30100/ +servicecomb: + kie: + serverUri: https://172.16.8.7:30103 + service: + registry: + autodiscovery: true + refreshInterval: 3000 + firstRefreshInterval: 0 + +service_description: + name: testDemo + environment: testing diff --git a/dynamic-config/pom.xml b/dynamic-config/pom.xml index afb0a20..71eaf00 100644 --- a/dynamic-config/pom.xml +++ b/dynamic-config/pom.xml @@ -35,6 +35,7 @@ <module>config-apollo</module> <module>config-cc</module> <module>config-nacos</module> + <module>config-kie</module> </modules> </project> diff --git a/handlers/handler-router/pom.xml b/handlers/handler-router/pom.xml index f5b02d1..73ac5a9 100644 --- a/handlers/handler-router/pom.xml +++ b/handlers/handler-router/pom.xml @@ -27,6 +27,7 @@ <modelVersion>4.0.0</modelVersion> <artifactId>handler-router</artifactId> + <name>Java Chassis::Handlers::Router</name> <dependencies> <dependency>