add apollo client support
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/common/exception/ConfigurationException.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/common/exception/ConfigurationException.java
new file mode 100644
index 0000000..89fbb0b
--- /dev/null
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/common/exception/ConfigurationException.java
@@ -0,0 +1,25 @@
+/*
+ * 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.dubbo.admin.common.exception;
+
+public class ConfigurationException extends RuntimeException{
+
+    public ConfigurationException(String message) {
+        super(message);
+    }
+}
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/config/ConfigCenter.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/config/ConfigCenter.java
index 6f6f13f..31af96f 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/config/ConfigCenter.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/config/ConfigCenter.java
@@ -18,9 +18,12 @@
 package org.apache.dubbo.admin.config;
 
 import org.apache.commons.lang3.StringUtils;
+import org.apache.dubbo.admin.common.exception.ConfigurationException;
+import org.apache.dubbo.admin.common.exception.ParamValidationException;
 import org.apache.dubbo.admin.common.util.Constants;
 import org.apache.dubbo.admin.data.config.GovernanceConfiguration;
 import org.apache.dubbo.admin.data.metadata.MetaDataCollector;
+import org.apache.dubbo.admin.data.metadata.impl.NoOpMetadataCollector;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.registry.Registry;
@@ -59,7 +62,8 @@
      */
     @Bean("governanceConfiguration")
     GovernanceConfiguration getDynamicConfiguration() {
-        GovernanceConfiguration dynamicConfiguration = ExtensionLoader.getExtensionLoader(GovernanceConfiguration.class).getDefaultExtension();
+        GovernanceConfiguration dynamicConfiguration = null;
+
         if (StringUtils.isNotEmpty(configCenter)) {
             configCenterUrl = formUrl(configCenter, group);
             dynamicConfiguration = ExtensionLoader.getExtensionLoader(GovernanceConfiguration.class).getExtension(configCenterUrl.getProtocol());
@@ -77,6 +81,17 @@
                 });
             }
         }
+        if (dynamicConfiguration == null) {
+            if (StringUtils.isNotEmpty(registryAddress)) {
+                registryUrl = formUrl(registryAddress, group);
+                dynamicConfiguration = ExtensionLoader.getExtensionLoader(GovernanceConfiguration.class).getExtension(registryUrl.getProtocol());
+                dynamicConfiguration.setUrl(registryUrl);
+                dynamicConfiguration.init();
+            } else {
+                throw new ConfigurationException("Either configcenter or registry address is needed");
+                //throw exception
+            }
+        }
         return dynamicConfiguration;
     }
 
@@ -88,6 +103,9 @@
     Registry getRegistry() {
         Registry registry = null;
         if (registryUrl == null) {
+            if (StringUtils.isNotEmpty(registryAddress)) {
+                throw new ConfigurationException("Either configcenter or registry address is needed");
+            }
             registryUrl = formUrl(registryAddress, group);
         }
         RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
@@ -101,7 +119,7 @@
     @Bean
     @DependsOn("governanceConfiguration")
     MetaDataCollector getMetadataCollector() {
-        MetaDataCollector metaDataCollector = ExtensionLoader.getExtensionLoader(MetaDataCollector.class).getDefaultExtension();
+        MetaDataCollector metaDataCollector = new NoOpMetadataCollector();
         if (metadataUrl != null) {
             metaDataCollector = ExtensionLoader.getExtensionLoader(MetaDataCollector.class).getExtension(metadataUrl.getProtocol());
             metaDataCollector.setUrl(metadataUrl);
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/controller/ServiceController.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/controller/ServiceController.java
index ddc8831..704d7e6 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/controller/ServiceController.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/controller/ServiceController.java
@@ -120,12 +120,14 @@
                                                                       info.get(Constants.VERSION_KEY),
                                                                       info.get(Constants.GROUP_KEY), Constants.PROVIDER_SIDE, application);
         String metadata = providerService.getProviderMetaData(identifier);
-        Gson gson = new Gson();
-        FullServiceDefinition serviceDefinition = gson.fromJson(metadata, FullServiceDefinition.class);
         ServiceDetailDTO serviceDetailDTO = new ServiceDetailDTO();
-        serviceDetailDTO.setConsumers(consumers);
-        serviceDetailDTO.setProviders(providers);
-        serviceDetailDTO.setMetadata(serviceDefinition);
+        if (metadata != null) {
+            Gson gson = new Gson();
+            FullServiceDefinition serviceDefinition = gson.fromJson(metadata, FullServiceDefinition.class);
+            serviceDetailDTO.setConsumers(consumers);
+            serviceDetailDTO.setProviders(providers);
+            serviceDetailDTO.setMetadata(serviceDefinition);
+        }
         return serviceDetailDTO;
     }
 }
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/data/config/GovernanceConfiguration.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/data/config/GovernanceConfiguration.java
index 5f86357..8fb75ab 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/data/config/GovernanceConfiguration.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/data/config/GovernanceConfiguration.java
@@ -34,4 +34,10 @@
 
     boolean deleteConfig(String key);
 
+    String setConfig(String group, String key, String value);
+
+    String getConfig(String group, String key);
+
+    boolean deleteConfig(String group, String key);
+
 }
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/data/config/impl/ApolloConfiguration.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/data/config/impl/ApolloConfiguration.java
index cb05e6a..2bc0f90 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/data/config/impl/ApolloConfiguration.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/data/config/impl/ApolloConfiguration.java
@@ -18,14 +18,30 @@
 package org.apache.dubbo.admin.data.config.impl;
 
 import com.ctrip.framework.apollo.openapi.client.ApolloOpenApiClient;
+import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO;
 import org.apache.dubbo.admin.data.config.GovernanceConfiguration;
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.SPI;
 import org.springframework.beans.factory.annotation.Value;
 
+@SPI("apollo")
 public class ApolloConfiguration implements GovernanceConfiguration {
 
     @Value("${dubbo.apollo.token}")
     private String token;
+
+    @Value("${dubbo.apollo.cluster}")
+    private String cluster;
+
+    @Value("${dubbo.apollo.namespace}")
+    private String namespace;
+
+    @Value("${dubbo.apollo.env}")
+    private String env;
+
+    @Value("${dubbo.apollo.appId}")
+    private String appId;
+
     private URL url;
     private ApolloOpenApiClient client;
 
@@ -47,16 +63,50 @@
 
     @Override
     public String setConfig(String key, String value) {
-        return null;
+        return setConfig(null, key, value);
     }
 
     @Override
     public String getConfig(String key) {
-        return null;
+        return getConfig(null, key);
     }
 
     @Override
     public boolean deleteConfig(String key) {
-        return false;
+        return deleteConfig(null, key);
+    }
+
+    @Override
+    public String setConfig(String group, String key, String value) {
+        if (group == null) {
+            group = namespace;
+        }
+        OpenItemDTO openItemDTO = new OpenItemDTO();
+        openItemDTO.setKey(key);
+        openItemDTO.setValue(value);
+        client.createItem(appId, env, cluster, group, openItemDTO);
+        return value;
+    }
+
+    @Override
+    public String getConfig(String group, String key) {
+        if (group == null) {
+            group = namespace;
+        }
+        OpenItemDTO openItemDTO =  client.getItem(appId, env, cluster, group, key);
+        if (openItemDTO != null) {
+            return openItemDTO.getValue();
+        }
+        return null;
+    }
+
+    @Override
+    public boolean deleteConfig(String group, String key) {
+        if (group == null) {
+            group = namespace;
+        }
+        //TODO user login user name as the operator
+        client.removeItem(appId, env, cluster, group, key, "admin");
+        return true;
     }
 }
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/data/config/impl/NoOpConfiguration.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/data/config/impl/NoOpConfiguration.java
index 324bf8f..08cb489 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/data/config/impl/NoOpConfiguration.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/data/config/impl/NoOpConfiguration.java
@@ -51,4 +51,19 @@
     public boolean deleteConfig(String key) {
         return false;
     }
+
+    @Override
+    public String setConfig(String group, String key, String value) {
+        return null;
+    }
+
+    @Override
+    public String getConfig(String group, String key) {
+        return null;
+    }
+
+    @Override
+    public boolean deleteConfig(String group, String key) {
+        return false;
+    }
 }
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/data/config/impl/ZookeeperConfiguration.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/data/config/impl/ZookeeperConfiguration.java
index 06770fb..95e7988 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/data/config/impl/ZookeeperConfiguration.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/data/config/impl/ZookeeperConfiguration.java
@@ -24,6 +24,8 @@
 import org.apache.dubbo.admin.data.config.GovernanceConfiguration;
 import org.apache.dubbo.common.URL;
 
+import javax.swing.*;
+
 public class ZookeeperConfiguration implements GovernanceConfiguration {
     private CuratorFramework zkClient;
     private URL url;
@@ -52,7 +54,22 @@
 
     @Override
     public String setConfig(String key, String value) {
-        String path = getNodePath(key);
+        return setConfig(null, key, value);
+    }
+
+    @Override
+    public String getConfig(String key) {
+        return getConfig(null, key);
+    }
+
+    @Override
+    public boolean deleteConfig(String key) {
+        return deleteConfig(null, key);
+    }
+
+    @Override
+    public String setConfig(String group, String key, String value) {
+        String path = getNodePath(key, group);
         try {
             if (zkClient.checkExists().forPath(path) == null) {
                 zkClient.create().creatingParentsIfNeeded().forPath(path);
@@ -66,8 +83,8 @@
     }
 
     @Override
-    public String getConfig(String key) {
-        String path = getNodePath(key);
+    public String getConfig(String group, String key) {
+        String path = getNodePath(key, group);
 
         try {
             if (zkClient.checkExists().forPath(path) == null) {
@@ -81,8 +98,8 @@
     }
 
     @Override
-    public boolean deleteConfig(String key) {
-        String path = getNodePath(key);
+    public boolean deleteConfig(String group, String key) {
+        String path = getNodePath(key, group);
         try {
             zkClient.delete().forPath(path);
         } catch (Exception e) {
@@ -91,11 +108,18 @@
         return true;
     }
 
-    private String getNodePath(String path) {
-        return toRootDir() + path;
+    private String getNodePath(String path, String group) {
+        return toRootDir(group) + path;
     }
 
-    private String toRootDir() {
+    private String toRootDir(String group) {
+        if (group != null) {
+            if (!group.startsWith(Constants.PATH_SEPARATOR)) {
+                root = Constants.PATH_SEPARATOR + group;
+            } else {
+                root = group;
+            }
+        }
         if (root.equals(Constants.PATH_SEPARATOR)) {
             return root;
         }
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/impl/OverrideServiceImpl.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/impl/OverrideServiceImpl.java
index dc4eef6..519696c 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/impl/OverrideServiceImpl.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/impl/OverrideServiceImpl.java
@@ -23,7 +23,6 @@
 import org.apache.dubbo.admin.common.util.YamlParser;
 import org.apache.dubbo.admin.model.domain.LoadBalance;
 import org.apache.dubbo.admin.model.domain.Override;
-import org.apache.dubbo.admin.model.dto.Config;
 import org.apache.dubbo.admin.model.domain.Weight;
 import org.apache.dubbo.admin.model.dto.BalancingDTO;
 import org.apache.dubbo.admin.model.dto.DynamicConfigDTO;
@@ -97,7 +96,7 @@
         }
         configs.addAll(update.getConfigs());
         overrideDTO.setConfigs(configs);
-        dynamicConfiguration.setConfig(path, YamlParser.dumpObject(update));
+        dynamicConfiguration.setConfig(path, YamlParser.dumpObject(overrideDTO));
 
         //for 2.6
         if (StringUtils.isNotEmpty(update.getService())) {
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/impl/ProviderServiceImpl.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/impl/ProviderServiceImpl.java
index 832f17b..09670c8 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/impl/ProviderServiceImpl.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/impl/ProviderServiceImpl.java
@@ -16,7 +16,9 @@
  */
 package org.apache.dubbo.admin.service.impl;
 
+import com.google.common.collect.Iterables;
 import com.google.gson.Gson;
+import org.apache.dubbo.admin.common.exception.ParamValidationException;
 import org.apache.dubbo.admin.common.util.ConvertUtil;
 import org.apache.dubbo.admin.common.util.Pair;
 import org.apache.dubbo.admin.common.util.ParseUtils;
@@ -31,6 +33,7 @@
 import org.apache.dubbo.metadata.identifier.MetadataIdentifier;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
+import org.yaml.snakeyaml.events.Event;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -39,6 +42,8 @@
 import java.util.Map.Entry;
 import java.util.concurrent.ConcurrentMap;
 
+import static org.apache.dubbo.common.Constants.SPECIFICATION_VERSION_KEY;
+
 /**
  * IbatisProviderService
  *
@@ -398,22 +403,21 @@
     @Override
     public String findVersionInApplication(String application) {
         List<String> services = findServicesByApplication(application);
+        if (services == null || services.size() == 0) {
+            throw new ParamValidationException("there is no service for application: " + application);
+        }
         return findServiceVersion(services.get(0), application);
     }
 
     @Override
     public String findServiceVersion(String serviceName, String application) {
         String version = "2.6";
-        serviceName = serviceName.replace("*", "/");
-        Map<String, String> info = ConvertUtil.serviceName2Map(serviceName);
-        MetadataIdentifier identifier = new MetadataIdentifier(info.get(Constants.INTERFACE_KEY),
-                info.get(Constants.VERSION_KEY),
-                info.get(Constants.GROUP_KEY), Constants.PROVIDER_SIDE, application);
-        String metadata = getProviderMetaData(identifier);
-        Gson gson = new Gson();
-        if (StringUtils.isNoneEmpty(metadata)) {
-            FullServiceDefinition serviceDefinition = gson.fromJson(metadata, FullServiceDefinition.class);
-            version = serviceDefinition.getParameters().get("specVersion");
+        Map<String, URL> result = findProviderUrlByAppandService(application, serviceName);
+        if (result != null && result.size() > 0) {
+            URL url = result.values().stream().findFirst().get();
+            if (url.getParameter(Constants.SPECIFICATION_VERSION_KEY) != null) {
+                version = url.getParameter(Constants.SPECIFICATION_VERSION_KEY);
+            }
         }
         return version;
     }
diff --git a/dubbo-admin-backend/src/main/resources/application.properties b/dubbo-admin-backend/src/main/resources/application.properties
index 294b80e..892d167 100644
--- a/dubbo-admin-backend/src/main/resources/application.properties
+++ b/dubbo-admin-backend/src/main/resources/application.properties
@@ -17,4 +17,8 @@
 
 dubbo.registry.group=dubbo
 dubbo.configcenter=zookeeper://127.0.0.1:2181
-dubbo.apollo.token=xxxxx
\ No newline at end of file
+dubbo.apollo.token=e16e5cd903fd0c97a116c873b448544b9d086de9
+dubbo.apollo.appId=test
+dubbo.apollo.env=dev
+dubbo.apollo.cluster=default
+dubbo.apollo.namespace=dubbo
diff --git a/dubbo-admin-frontend/src/components/governance/AccessControl.vue b/dubbo-admin-frontend/src/components/governance/AccessControl.vue
index 5e26f27..3b2dcab 100644
--- a/dubbo-admin-frontend/src/components/governance/AccessControl.vue
+++ b/dubbo-admin-frontend/src/components/governance/AccessControl.vue
@@ -307,7 +307,7 @@
       let doc = yaml.load(this.modal.content)
       this.filter = ''
       if (this.modal.service === '' && this.modal.service === null) {
-        this.$notify.error("Either service or application is needed")
+        this.$notify.error('Either service or application is needed')
         return
       }
       var vm = this
diff --git a/dubbo-admin-frontend/src/components/governance/LoadBalance.vue b/dubbo-admin-frontend/src/components/governance/LoadBalance.vue
index 06ebce2..76f9425 100644
--- a/dubbo-admin-frontend/src/components/governance/LoadBalance.vue
+++ b/dubbo-admin-frontend/src/components/governance/LoadBalance.vue
@@ -270,7 +270,7 @@
         this.ruleText = this.verifyRuleText(this.ruleText)
         let balancing = yaml.safeLoad(this.ruleText)
         if (this.service === '' && this.application === '') {
-          this.$notify.error("Either service or application is needed")
+          this.$notify.error('Either service or application is needed')
           return
         }
         balancing.service = this.service
diff --git a/dubbo-admin-frontend/src/components/governance/Overrides.vue b/dubbo-admin-frontend/src/components/governance/Overrides.vue
index 6c773bc..32a1855 100644
--- a/dubbo-admin-frontend/src/components/governance/Overrides.vue
+++ b/dubbo-admin-frontend/src/components/governance/Overrides.vue
@@ -270,7 +270,7 @@
       saveItem: function () {
         let override = yaml.safeLoad(this.ruleText)
         if (this.service === '' && this.application === '') {
-          this.$notify.error("Either service or application is needed")
+          this.$notify.error('Either service or application is needed')
           return
         }
         override.service = this.service
@@ -361,6 +361,7 @@
         delete config.service
         delete config.application
         delete config.id
+        this.removeEmpty(config)
         this.ruleText = yaml.safeDump(config)
         this.readonly = readonly
         this.dialog = true
@@ -368,6 +369,12 @@
       setHeight: function () {
         this.height = window.innerHeight * 0.5
       },
+      removeEmpty: function (obj) {
+        Object.keys(obj).forEach(key => {
+          if (obj[key] && typeof obj[key] === 'object') this.removeEmpty(obj[key]);
+          else if (obj[key] == null) delete obj[key];
+        });
+      },
       deleteItem: function (warnStatus) {
         let id = warnStatus.id
         let operation = warnStatus.operation
diff --git a/dubbo-admin-frontend/src/components/governance/RoutingRule.vue b/dubbo-admin-frontend/src/components/governance/RoutingRule.vue
index 5357762..09cbd3f 100644
--- a/dubbo-admin-frontend/src/components/governance/RoutingRule.vue
+++ b/dubbo-admin-frontend/src/components/governance/RoutingRule.vue
@@ -295,7 +295,7 @@
       saveItem: function () {
         let rule = yaml.safeLoad(this.ruleText)
         if (this.service === '' && this.application === '') {
-          this.$notify.error("Either service or application is needed")
+          this.$notify.error('Either service or application is needed')
           return
         }
         rule.service = this.service
diff --git a/dubbo-admin-frontend/src/components/governance/TagRule.vue b/dubbo-admin-frontend/src/components/governance/TagRule.vue
index e7bd9ce..ee9c3a7 100644
--- a/dubbo-admin-frontend/src/components/governance/TagRule.vue
+++ b/dubbo-admin-frontend/src/components/governance/TagRule.vue
@@ -194,7 +194,7 @@
       saveItem: function () {
         let rule = yaml.safeLoad(this.ruleText)
         if (this.application === '') {
-          this.$notify.error("application is required")
+          this.$notify.error('application is required')
           return
         }
         rule.application = this.application
diff --git a/dubbo-admin-frontend/src/components/governance/WeightAdjust.vue b/dubbo-admin-frontend/src/components/governance/WeightAdjust.vue
index ebedf5e..737daad 100644
--- a/dubbo-admin-frontend/src/components/governance/WeightAdjust.vue
+++ b/dubbo-admin-frontend/src/components/governance/WeightAdjust.vue
@@ -274,7 +274,7 @@
       saveItem: function () {
         let weight = yaml.safeLoad(this.ruleText)
         if (this.service === '' && this.application === '') {
-          this.$notify.error("Either service or application is needed")
+          this.$notify.error('Either service or application is needed')
           return
         }
         weight.service = this.service