Merge branch 'develop' into service-test
diff --git a/dubbo-admin-backend/pom.xml b/dubbo-admin-backend/pom.xml
index 671f865..8d04e90 100644
--- a/dubbo-admin-backend/pom.xml
+++ b/dubbo-admin-backend/pom.xml
@@ -96,6 +96,12 @@
         </dependency>
 
         <dependency>
+            <groupId>org.apache.curator</groupId>
+            <artifactId>curator-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
             <groupId>io.springfox</groupId>
             <artifactId>springfox-swagger2</artifactId>
         </dependency>
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/common/util/MD5Util.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/common/util/CoderUtil.java
similarity index 82%
rename from dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/common/util/MD5Util.java
rename to dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/common/util/CoderUtil.java
index ad01650..1245168 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/common/util/MD5Util.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/common/util/CoderUtil.java
@@ -17,15 +17,16 @@
 
 package org.apache.dubbo.admin.common.util;
 
+import org.apache.dubbo.common.io.Bytes;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 
-public class MD5Util {
+public class CoderUtil {
 
-    private static final Logger logger = LoggerFactory.getLogger(MD5Util.class);
+    private static final Logger logger = LoggerFactory.getLogger(CoderUtil.class);
     private static MessageDigest md;
     private static final char[] hexCode = "0123456789ABCDEF".toCharArray();
 
@@ -55,6 +56,16 @@
         return hash;
     }
 
+    public static String MD5_32bit(byte[] input) {
+        if (input == null || input.length == 0) {
+            return null;
+        }
+        md.update(input);
+        byte[] digest = md.digest();
+        String hash = convertToString(digest);
+        return hash;
+    }
+
     private static String convertToString(byte[] data) {
         StringBuilder r = new StringBuilder(data.length * 2);
         for (byte b : data) {
@@ -64,7 +75,7 @@
         return r.toString();
     }
 
-    public static void main(String[] args) {
-        System.out.println(MD5_16bit("fwjioejfiowejfiowjfiwfjowejfei"));
+    public static String decodeBase64(String source) {
+        return new String(Bytes.base642bytes(source));
     }
 }
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/common/util/Constants.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/common/util/Constants.java
index 86d9d2b..ef2ef89 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/common/util/Constants.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/common/util/Constants.java
@@ -29,6 +29,7 @@
     public static final String PATH_SEPARATOR = "/";
     public static final String GROUP_KEY = "group";
     public static final String CONFIG_KEY = "config";
+    public static final String DUBBO_PROPERTY = "dubbo.properties";
     public static final String PROVIDER_SIDE = "provider";
     public static final String CONSUMER_SIDE = "consumer";
     public static final String CATEGORY_KEY = "category";
@@ -51,7 +52,8 @@
     public static final String VERSION_KEY = "version";
     public static final String PROVIDERS_CATEGORY = "providers";
     public static final String CONSUMERS_CATEGORY = "consumers";
-    public static final String SPECIFICATION_VERSION_KEY = "specVersion";
+    public static final String SPECIFICATION_VERSION_KEY = "release";
+    public static final String GLOBAL_CONFIG = "global";
     public static final Set<String> CONFIGS = new HashSet<>();
 
     static {
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 a3c6405..491b35e 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
@@ -23,12 +23,14 @@
 import org.apache.dubbo.admin.registry.config.GovernanceConfiguration;
 import org.apache.dubbo.admin.registry.metadata.MetaDataCollector;
 import org.apache.dubbo.admin.registry.metadata.impl.NoOpMetadataCollector;
+import org.apache.dubbo.admin.service.ManagementService;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.registry.Registry;
 import org.apache.dubbo.registry.RegistryFactory;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -41,18 +43,25 @@
 public class ConfigCenter {
 
 
-    @Value("${dubbo.config-center:}")
-    private String configCenter;
-    @Value("${dubbo.config-center.username:}")
-    private String username;
-    @Value("${dubbo.config-center.password:}")
-    private String password;
 
-    @Value("${dubbo.registry.address:}")
+    //centers in dubbo 2.7
+    @Value("${admin.config-center:}")
+    private String configCenter;
+
+    @Value("${admin.registry.address:}")
     private String registryAddress;
-    @Value("${dubbo.registry.group:}")
+
+    @Value("${admin.metadata.address:}")
+    private String metadataAddress;
+
+    @Value("${admin.registry.group:}")
     private String group;
 
+    @Value("${admin.config-center.username:}")
+    private String username;
+    @Value("${admin.config-center.password:}")
+
+    private String password;
     private static String globalConfigPath = "config/dubbo/dubbo.properties";
 
     private static final Logger logger = LoggerFactory.getLogger(ConfigCenter.class);
@@ -62,6 +71,7 @@
     private URL metadataUrl;
 
 
+
     /*
      * generate dynamic configuration client
      */
@@ -79,7 +89,8 @@
             if (StringUtils.isNotEmpty(config)) {
                 Arrays.stream(config.split("\n")).forEach( s -> {
                     if(s.startsWith(Constants.REGISTRY_ADDRESS)) {
-                        registryUrl = formUrl(s.split("=")[1].trim(), group, username, password);
+                        String registryAddress = s.split("=")[1].trim();
+                        registryUrl = formUrl(registryAddress, group, username, password);
                     } else if (s.startsWith(Constants.METADATA_ADDRESS)) {
                         metadataUrl = formUrl(s.split("=")[1].trim(), group, username, password);
                     }
@@ -126,6 +137,11 @@
     @DependsOn("governanceConfiguration")
     MetaDataCollector getMetadataCollector() {
         MetaDataCollector metaDataCollector = new NoOpMetadataCollector();
+        if (metadataUrl == null) {
+            if (StringUtils.isNotEmpty(metadataAddress)) {
+                metadataUrl = formUrl(metadataAddress, group, username, password);
+            }
+        }
         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/ManagementController.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/controller/ManagementController.java
new file mode 100644
index 0000000..c846ce6
--- /dev/null
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/controller/ManagementController.java
@@ -0,0 +1,104 @@
+/*
+ * 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.controller;
+
+import org.apache.dubbo.admin.common.exception.ParamValidationException;
+import org.apache.dubbo.admin.common.exception.ResourceNotFoundException;
+import org.apache.dubbo.admin.common.util.Constants;
+import org.apache.dubbo.admin.model.dto.ConfigDTO;
+import org.apache.dubbo.admin.service.ManagementService;
+import org.apache.dubbo.admin.service.ProviderService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+@RestController
+@RequestMapping("/api/{env}/manage")
+public class ManagementController {
+
+    private final ManagementService managementService;
+    private final ProviderService providerService;
+    private static Pattern CLASS_NAME_PATTERN = Pattern.compile("([a-zA-Z_$][a-zA-Z\\d_$]*\\.)*[a-zA-Z_$][a-zA-Z\\d_$]*");
+
+
+    @Autowired
+    public ManagementController(ManagementService managementService, ProviderService providerService) {
+        this.managementService = managementService;
+        this.providerService = providerService;
+    }
+
+    @RequestMapping(value ="/config", method = RequestMethod.POST)
+    @ResponseStatus(HttpStatus.CREATED)
+    public boolean createConfig(@RequestBody ConfigDTO config, @PathVariable String env) {
+        managementService.setConfig(config);
+        return true;
+    }
+
+    @RequestMapping(value = "/config/{key}", method = RequestMethod.PUT)
+    public boolean updateConfig(@PathVariable String key, @RequestBody ConfigDTO configDTO, @PathVariable String env) {
+        if (key == null) {
+            throw new ParamValidationException("Unknown ID!");
+        }
+        String exitConfig = managementService.getConfig(key);
+        if (exitConfig == null) {
+            throw new ResourceNotFoundException("Unknown ID!"); }
+        return managementService.updateConfig(configDTO);
+    }
+
+    @RequestMapping(value = "/config/{key}", method = RequestMethod.GET)
+    public List<ConfigDTO> getConfig(@PathVariable String key, @PathVariable String env) {
+        Set<String> query = new HashSet<>();
+        List<ConfigDTO> configDTOs = new ArrayList<>();
+        if (key.equals(Constants.ANY_VALUE)) {
+            query = providerService.findApplications();
+            query.add("global");
+        } else {
+            query.add(key);
+        }
+        for (String q : query) {
+            String config = managementService.getConfig(q);
+            if (config == null) {
+                continue;
+            }
+            ConfigDTO configDTO = new ConfigDTO();
+            configDTO.setKey(q);
+            configDTO.setConfig(config);
+            configDTO.setPath(managementService.getConfigPath(q));
+            if (Constants.GLOBAL_CONFIG.equals(q)) {
+                configDTO.setScope(Constants.GLOBAL_CONFIG);
+            } else if(CLASS_NAME_PATTERN.matcher(q).matches()){
+                configDTO.setScope(Constants.SERVICE);
+            } else {
+                configDTO.setScope(Constants.APPLICATION);
+            }
+            configDTOs.add(configDTO);
+        }
+        return configDTOs;
+    }
+
+    @RequestMapping(value = "/config/{key}", method = RequestMethod.DELETE)
+    public boolean deleteConfig(@PathVariable String key, @PathVariable String env) {
+        return managementService.deleteConfig(key);
+    }
+}
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 87856ef..dfbc607 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
@@ -17,6 +17,7 @@
 
 package org.apache.dubbo.admin.controller;
 
+import com.ctrip.framework.apollo.core.enums.Env;
 import com.google.gson.Gson;
 import org.apache.dubbo.admin.common.util.ConvertUtil;
 import org.apache.dubbo.admin.model.domain.Consumer;
@@ -26,7 +27,6 @@
 import org.apache.dubbo.admin.service.ConsumerService;
 import org.apache.dubbo.admin.service.ProviderService;
 import org.apache.dubbo.admin.common.util.Constants;
-import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.metadata.definition.model.FullServiceDefinition;
 import org.apache.dubbo.metadata.identifier.MetadataIdentifier;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -38,7 +38,7 @@
 
 
 @RestController
-@RequestMapping("/api/{env}/service")
+@RequestMapping("/api/{env}")
 public class ServiceController {
 
     private final ProviderService providerService;
@@ -50,7 +50,7 @@
         this.consumerService = consumerService;
     }
 
-    @RequestMapping(method = RequestMethod.GET)
+    @RequestMapping( value = "/service", method = RequestMethod.GET)
     public Set<ServiceDTO> searchService(@RequestParam String pattern,
                                          @RequestParam String filter,@PathVariable String env) {
         return providerService.getServiceDTOS(pattern, filter, env);
@@ -58,7 +58,7 @@
 
 
 
-    @RequestMapping(value = "/{service}", method = RequestMethod.GET)
+    @RequestMapping(value = "/service/{service}", method = RequestMethod.GET)
     public ServiceDetailDTO serviceDetail(@PathVariable String service, @PathVariable String env) {
         service = service.replace(Constants.ANY_VALUE, Constants.PATH_SEPARATOR);
         List<Provider> providers = providerService.findByService(service);
@@ -88,4 +88,14 @@
         serviceDetailDTO.setApplication(application);
         return serviceDetailDTO;
     }
+
+    @RequestMapping(value = "/services", method = RequestMethod.GET)
+    public Set<String> allServices(@PathVariable String env) {
+        return providerService.findServices();
+    }
+
+    @RequestMapping(value = "/applications", method = RequestMethod.GET)
+    public Set<String> allApplications(@PathVariable String env) {
+        return providerService.findApplications();
+    }
 }
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/model/dto/ConfigDTO.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/model/dto/ConfigDTO.java
new file mode 100644
index 0000000..439fab8
--- /dev/null
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/model/dto/ConfigDTO.java
@@ -0,0 +1,58 @@
+/*
+ * 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.model.dto;
+
+public class ConfigDTO {
+
+    private String key;
+    private String config;
+    private String scope;
+    private String path;
+
+    public String getKey() {
+        return key;
+    }
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+    public String getConfig() {
+        return config;
+    }
+
+    public void setConfig(String config) {
+        this.config = config;
+    }
+
+    public String getScope() {
+        return scope;
+    }
+
+    public void setScope(String scope) {
+        this.scope = scope;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+}
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/GovernanceConfiguration.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/GovernanceConfiguration.java
index c6ed397..21e739b 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/GovernanceConfiguration.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/GovernanceConfiguration.java
@@ -40,4 +40,8 @@
 
     boolean deleteConfig(String group, String key);
 
+    String getPath(String key);
+
+    String getPath(String group, String key);
+
 }
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/impl/ApolloConfiguration.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/impl/ApolloConfiguration.java
index 13d4b2e..58ce20c 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/impl/ApolloConfiguration.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/impl/ApolloConfiguration.java
@@ -27,19 +27,19 @@
 @SPI("apollo")
 public class ApolloConfiguration implements GovernanceConfiguration {
 
-    @Value("${dubbo.apollo.token}")
+    @Value("${admin.apollo.token}")
     private String token;
 
-    @Value("${dubbo.apollo.cluster}")
+    @Value("${admin.apollo.cluster}")
     private String cluster;
 
-    @Value("${dubbo.apollo.namespace}")
+    @Value("${admin.apollo.namespace}")
     private String namespace;
 
-    @Value("${dubbo.apollo.env}")
+    @Value("${admin.apollo.env}")
     private String env;
 
-    @Value("${dubbo.apollo.appId}")
+    @Value("${admin.apollo.appId}")
     private String appId;
 
     private URL url;
@@ -109,4 +109,14 @@
         client.removeItem(appId, env, cluster, group, key, "admin");
         return true;
     }
+
+    @Override
+    public String getPath(String key) {
+        return null;
+    }
+
+    @Override
+    public String getPath(String group, String key) {
+        return null;
+    }
 }
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/impl/NoOpConfiguration.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/impl/NoOpConfiguration.java
index 85dca2f..5c4fd18 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/impl/NoOpConfiguration.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/impl/NoOpConfiguration.java
@@ -66,4 +66,14 @@
     public boolean deleteConfig(String group, String key) {
         return false;
     }
+
+    @Override
+    public String getPath(String key) {
+        return null;
+    }
+
+    @Override
+    public String getPath(String group, String key) {
+        return null;
+    }
 }
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/impl/ZookeeperConfiguration.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/impl/ZookeeperConfiguration.java
index 0ba847a..1eed438 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/impl/ZookeeperConfiguration.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/impl/ZookeeperConfiguration.java
@@ -17,16 +17,16 @@
 
 package org.apache.dubbo.admin.registry.config.impl;
 
+import org.apache.dubbo.admin.common.util.Constants;
+import org.apache.dubbo.admin.registry.config.GovernanceConfiguration;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+
 import org.apache.commons.lang3.StringUtils;
 import org.apache.curator.framework.CuratorFramework;
 import org.apache.curator.framework.CuratorFrameworkFactory;
 import org.apache.curator.retry.ExponentialBackoffRetry;
-import org.apache.dubbo.admin.common.util.Constants;
-import org.apache.dubbo.admin.registry.config.GovernanceConfiguration;
-import org.apache.dubbo.common.logger.Logger;
-import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.common.URL;
-
 
 public class ZookeeperConfiguration implements GovernanceConfiguration {
     private static final Logger logger = LoggerFactory.getLogger(ZookeeperConfiguration.class);
@@ -46,10 +46,13 @@
 
     @Override
     public void init() {
+        if (url == null) {
+            throw new IllegalStateException("server url is null, cannot init");
+        }
         CuratorFrameworkFactory.Builder zkClientBuilder = CuratorFrameworkFactory.builder().
                 connectString(url.getAddress()).
                 retryPolicy(new ExponentialBackoffRetry(1000, 3));
-        if(StringUtils.isNotEmpty(url.getUsername()) && StringUtils.isNotEmpty(url.getPassword())){
+        if (StringUtils.isNotEmpty(url.getUsername()) && StringUtils.isNotEmpty(url.getPassword())) {
             // add authorization
             String auth = url.getUsername() + ":" + url.getPassword();
             zkClientBuilder.authorization("digest", auth.getBytes());
@@ -80,6 +83,9 @@
 
     @Override
     public String setConfig(String group, String key, String value) {
+        if (key == null || value == null) {
+            throw new IllegalArgumentException("key or value cannot be null");
+        }
         String path = getNodePath(key, group);
         try {
             if (zkClient.checkExists().forPath(path) == null) {
@@ -95,6 +101,9 @@
 
     @Override
     public String getConfig(String group, String key) {
+        if (key == null) {
+            throw new IllegalArgumentException("key cannot be null");
+        }
         String path = getNodePath(key, group);
 
         try {
@@ -110,16 +119,33 @@
 
     @Override
     public boolean deleteConfig(String group, String key) {
+        if (key == null) {
+            throw new IllegalArgumentException("key cannot be null");
+        }
         String path = getNodePath(key, group);
         try {
             zkClient.delete().forPath(path);
         } catch (Exception e) {
             logger.error(e.getMessage(), e);
+            return false;
         }
         return true;
     }
 
+    @Override
+    public String getPath(String key) {
+        return getNodePath(key, null);
+    }
+
+    @Override
+    public String getPath(String group, String key) {
+        return getNodePath(key, group);
+    }
+
     private String getNodePath(String path, String group) {
+        if (path == null) {
+            throw new IllegalArgumentException("path cannot be null");
+        }
         return toRootDir(group) + path;
     }
 
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/ManagementService.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/ManagementService.java
new file mode 100644
index 0000000..9d0a388
--- /dev/null
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/ManagementService.java
@@ -0,0 +1,34 @@
+/*
+ * 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.service;
+
+import org.apache.dubbo.admin.model.dto.ConfigDTO;
+
+public interface ManagementService {
+
+
+    void setConfig(ConfigDTO config);
+
+    String getConfig(String key);
+
+    String getConfigPath(String key);
+
+    boolean updateConfig(ConfigDTO configDTO);
+
+    boolean deleteConfig(String key);
+}
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/ProviderService.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/ProviderService.java
index 01a79e4..b035b13 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/ProviderService.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/ProviderService.java
@@ -52,7 +52,7 @@
      *
      * @return list of all provider's service name
      */
-    List<String> findServices();
+    Set<String> findServices();
 
     String findServiceVersion(String serviceName, String application);
 
@@ -88,7 +88,7 @@
 
     List<String> findServicesByAddress(String providerAddress);
 
-    List<String> findApplications();
+    Set<String> findApplications();
 
     /**
      * Get provider list with specific application name.
@@ -117,4 +117,4 @@
      */
     Set<ServiceDTO> getServiceDTOS(String pattern, String filter, String env);
 
-}
\ No newline at end of file
+}
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/RegistryServerSync.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/RegistryServerSync.java
index 7a4a676..1cd7bb5 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/RegistryServerSync.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/RegistryServerSync.java
@@ -16,7 +16,7 @@
  */
 package org.apache.dubbo.admin.service;
 
-import org.apache.dubbo.admin.common.util.MD5Util;
+import org.apache.dubbo.admin.common.util.CoderUtil;
 import org.apache.dubbo.admin.common.util.Tool;
 import org.apache.dubbo.common.Constants;
 import org.apache.dubbo.common.URL;
@@ -134,7 +134,7 @@
                 if (URL_IDS_MAPPER.containsKey(url.toFullString())) {
                     ids.put(URL_IDS_MAPPER.get(url.toFullString()), url);
                 } else {
-                    String md5 = MD5Util.MD5_16bit(url.toFullString());
+                    String md5 = CoderUtil.MD5_16bit(url.toFullString());
                     ids.put(md5, url);
                     URL_IDS_MAPPER.putIfAbsent(url.toFullString(), md5);
                 }
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/impl/ManagementServiceImpl.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/impl/ManagementServiceImpl.java
new file mode 100644
index 0000000..d43e6de
--- /dev/null
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/impl/ManagementServiceImpl.java
@@ -0,0 +1,82 @@
+/*
+ * 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.service.impl;
+
+import org.apache.dubbo.admin.common.util.Constants;
+import org.apache.dubbo.admin.model.dto.ConfigDTO;
+import org.apache.dubbo.admin.service.ManagementService;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ManagementServiceImpl extends AbstractService implements ManagementService {
+
+
+    private static String globalConfigPath = "config/dubbo/dubbo.properties";
+
+    @Override
+    public void setConfig(ConfigDTO config) {
+        if (Constants.GLOBAL_CONFIG.equals(config.getKey())) {
+            dynamicConfiguration.setConfig(globalConfigPath, config.getConfig());
+        } else {
+            dynamicConfiguration.setConfig(getPath(config.getKey()), config.getConfig());
+        }
+    }
+
+    @Override
+    public String getConfig(String key) {
+        if (Constants.GLOBAL_CONFIG.equals(key)) {
+            return dynamicConfiguration.getConfig(globalConfigPath);
+        }
+        return dynamicConfiguration.getConfig(getPath(key));
+    }
+
+    @Override
+    public String getConfigPath(String key) {
+        if (Constants.GLOBAL_CONFIG.equals(key)) {
+            return dynamicConfiguration.getPath(globalConfigPath);
+        }
+        return dynamicConfiguration.getPath(getPath(key));
+    }
+
+    @Override
+    public boolean updateConfig(ConfigDTO configDTO) {
+        String key = configDTO.getKey();
+        if (Constants.GLOBAL_CONFIG.equals(key)) {
+            dynamicConfiguration.setConfig(globalConfigPath, configDTO.getConfig());
+        } else {
+            dynamicConfiguration.setConfig(getPath(key), configDTO.getConfig());
+        }
+        return true;
+    }
+
+    @Override
+    public boolean deleteConfig(String key) {
+        if (Constants.GLOBAL_CONFIG.equals(key)) {
+            dynamicConfiguration.deleteConfig(globalConfigPath);
+        } else {
+            dynamicConfiguration.deleteConfig(getPath(key));
+        }
+        return true;
+    }
+
+    private String getPath(String key) {
+        return Constants.CONFIG_KEY + Constants.PATH_SEPARATOR + key + Constants.PATH_SEPARATOR
+                + Constants.DUBBO_PROPERTY;
+    }
+
+}
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 0786b8c..98e3e63 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
@@ -253,8 +253,8 @@
     }
 
     @Override
-    public List<String> findServices() {
-        List<String> ret = new ArrayList<String>();
+    public Set<String> findServices() {
+        Set<String> ret = new HashSet<>();
         ConcurrentMap<String, Map<String, URL>> providerUrls = getRegistryCache().get(Constants.PROVIDERS_CATEGORY);
         if (providerUrls != null){
             ret.addAll(providerUrls.keySet());
@@ -413,8 +413,8 @@
     }
 
     @Override
-    public List<String> findApplications() {
-        List<String> ret = new ArrayList<String>();
+    public Set<String> findApplications() {
+        Set<String> ret = new HashSet<>();
         ConcurrentMap<String, Map<String, URL>> providerUrls = getRegistryCache().get(Constants.PROVIDERS_CATEGORY);
         if (providerUrls == null){
             return ret;
@@ -568,7 +568,7 @@
             }
         } else {
             // filter with fuzzy search
-            List<String> candidates = Collections.emptyList();
+            Set<String> candidates = Collections.emptySet();
             if (Constants.SERVICE.equals(pattern)) {
                 candidates = findServices();
             } else if (Constants.APPLICATION.equals(pattern)) {
diff --git a/dubbo-admin-backend/src/main/resources/application.properties b/dubbo-admin-backend/src/main/resources/application.properties
index 28fa8d0..514345f 100644
--- a/dubbo-admin-backend/src/main/resources/application.properties
+++ b/dubbo-admin-backend/src/main/resources/application.properties
@@ -15,13 +15,16 @@
 # limitations under the License.
 #
 
-dubbo.config-center.username=username
-dubbo.config-center.password=password
-dubbo.config-center=zookeeper://127.0.0.1:2181
-dubbo.registry.group=dubbo
-dubbo.registry.address=zookeeper://127.0.0.1:2181
-dubbo.apollo.token=e16e5cd903fd0c97a116c873b448544b9d086de9
-dubbo.apollo.appId=test
-dubbo.apollo.env=dev
-dubbo.apollo.cluster=default
-dubbo.apollo.namespace=dubbo
+# centers in dubbo2.7
+admin.registry.address=zookeeper://127.0.0.1:2181
+admin.config-center=zookeeper://127.0.0.1:2181
+admin.metadata.address=zookeeper://127.0.0.1:2181
+
+
+
+admin.registry.group=dubbo
+admin.apollo.token=e16e5cd903fd0c97a116c873b448544b9d086de9
+admin.apollo.appId=test
+admin.apollo.env=dev
+admin.apollo.cluster=default
+admin.apollo.namespace=dubbo
diff --git a/dubbo-admin-backend/src/test/java/org/apache/dubbo/admin/registry/config/impl/ZookeeperConfigurationTest.java b/dubbo-admin-backend/src/test/java/org/apache/dubbo/admin/registry/config/impl/ZookeeperConfigurationTest.java
new file mode 100644
index 0000000..7c2c90f
--- /dev/null
+++ b/dubbo-admin-backend/src/test/java/org/apache/dubbo/admin/registry/config/impl/ZookeeperConfigurationTest.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.dubbo.admin.registry.config.impl;
+
+import org.apache.dubbo.admin.common.util.Constants;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.NetUtils;
+
+import org.apache.curator.test.TestingServer;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class ZookeeperConfigurationTest {
+
+    private TestingServer zkServer;
+    private ZookeeperConfiguration configuration;
+    private URL registryUrl;
+
+    @Before
+    public void setup() throws Exception {
+        int zkServerPort = NetUtils.getAvailablePort();
+        zkServer = new TestingServer(zkServerPort, true);
+        registryUrl = URL.valueOf("zookeeper://localhost:" + zkServerPort);
+
+        configuration = new ZookeeperConfiguration();
+        try {
+            configuration.init();
+            fail("init should fail before setting registryUrl");
+        } catch (IllegalStateException e) {
+        }
+
+        configuration.setUrl(registryUrl);
+        configuration.init();
+    }
+
+    @Test
+    public void testGetSetConfig() {
+        configuration.setConfig("test_key", "test_value");
+        assertEquals("test_value", configuration.getConfig("test_key"));
+        assertEquals(null, configuration.getConfig("not_exist_key"));
+
+
+        configuration.setConfig("test_group", "test_key", "test_group_value");
+        assertEquals("test_group_value", configuration.getConfig("test_group", "test_key"));
+
+        assertEquals(null, configuration.getConfig("test_group", "not_exist_key"));
+
+        try {
+            configuration.getConfig(null);
+            fail("should throw IllegalArgumentException for null key");
+        } catch (IllegalArgumentException e) {
+        }
+        try {
+            configuration.setConfig("test_null", null);
+            fail("should throw IllegalArgumentException for null key");
+        } catch (IllegalArgumentException e) {
+        }
+    }
+
+    @Test
+    public void testDeleteConfig() {
+        assertEquals(false, configuration.deleteConfig("not_exist_key"));
+        configuration.setConfig("test_delete", "test_value");
+        assertEquals("test_value", configuration.getConfig("test_delete"));
+        configuration.deleteConfig("test_delete");
+        assertEquals(null, configuration.getConfig("test_delete"));
+
+        assertEquals(false, configuration.deleteConfig("test_group", "not_exist_key"));
+        configuration.setConfig("test_group", "test_delete", "test_value");
+        assertEquals("test_value", configuration.getConfig("test_group", "test_delete"));
+        configuration.deleteConfig("test_group", "test_delete");
+        assertEquals(null, configuration.getConfig("test_group", "test_delete"));
+
+        try {
+            configuration.deleteConfig(null);
+            fail("should throw IllegalArgumentException for null key");
+        } catch (IllegalArgumentException e) {
+        }
+    }
+
+    @Test
+    public void testGetPath() {
+        assertEquals(Constants.PATH_SEPARATOR + Constants.DEFAULT_ROOT + Constants.PATH_SEPARATOR + "test_key",
+                configuration.getPath("test_key"));
+        try {
+            configuration.getPath(null);
+            fail("should throw IllegalArgumentException for null path");
+        } catch (IllegalArgumentException e) {
+        }
+    }
+
+    @After
+    public void tearDown() throws IOException {
+        zkServer.stop();
+    }
+
+}
diff --git a/dubbo-admin-frontend/src/api/menu.js b/dubbo-admin-frontend/src/api/menu.js
index 6f5ccac..05c058c 100644
--- a/dubbo-admin-frontend/src/api/menu.js
+++ b/dubbo-admin-frontend/src/api/menu.js
@@ -32,8 +32,8 @@
   },
   { title: 'serviceTest', path: '/test', icon: 'code', badge: 'feature' },
   { title: 'serviceMock', path: '/mock', icon: 'build', badge: 'feature' },
-  { title: 'metrics', path: '/metrics', icon: 'show_chart', badge: 'feature' }
-
+  { title: 'metrics', path: '/metrics', icon: 'show_chart', badge: 'feature' },
+  { title: 'configManage', path: '/management', icon: 'build', badge: 'feature' }
 ]
 
 export default Menu
diff --git a/dubbo-admin-frontend/src/components/Management.vue b/dubbo-admin-frontend/src/components/Management.vue
new file mode 100644
index 0000000..f80d03b
--- /dev/null
+++ b/dubbo-admin-frontend/src/components/Management.vue
@@ -0,0 +1,293 @@
+<!--
+  - 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.
+  -->
+
+<template>
+  <v-container grid-list-xl fluid >
+    <v-layout row wrap>
+      <v-flex xs12 >
+        <search id="serviceSearch" v-model="filter" :submit="submit" :label="$t('searchDubboConfig')" :hint="$t('configNameHint')"></search>
+      </v-flex>
+    </v-layout>
+    <v-flex lg12>
+      <v-card>
+        <v-toolbar flat color="transparent" class="elevation-0">
+          <v-toolbar-title><span class="headline">{{$t('searchResult')}}</span></v-toolbar-title>
+          <v-spacer></v-spacer>
+          <v-btn outline color="primary" @click.stop="openDialog" class="mb-2">{{$t('create')}}</v-btn>
+        </v-toolbar>
+
+        <v-card-text class="pa-0" >
+          <v-data-table
+            :headers="headers"
+            :items="dubboConfig"
+            hide-actions
+            class="elevation-0"
+          >
+            <template slot="items" slot-scope="props">
+              <td class="text-xs-left">
+                <v-tooltip bottom >
+                   <span slot="activator">
+                     {{props.item.key}}
+                   </span>
+                  <span>{{props.item.path}}</span>
+                </v-tooltip>
+              </td>
+              <td class="text-xs-left">
+                <v-chip
+                  :color="getColor(props.item.scope)"
+                  text-color="white">
+                  {{props.item.scope}}
+                </v-chip>
+              </td>
+              <td class="text-xs-center px-0">
+                <v-tooltip bottom v-for="op in operations" :key="op.id">
+                  <v-icon small class="mr-2" slot="activator" @click="itemOperation(op.icon, props.item)">
+                    {{op.icon}}
+                  </v-icon>
+                  <span>{{$t(op.tooltip)}}</span>
+                </v-tooltip>
+              </td>
+            </template>
+          </v-data-table>
+        </v-card-text>
+      </v-card>
+    </v-flex>
+
+    <v-dialog   v-model="dialog" width="800px" persistent >
+      <v-card>
+        <v-card-title class="justify-center">
+          <span class="headline">{{$t('createNewDubboConfig')}}</span>
+        </v-card-title>
+        <v-card-text >
+          <v-text-field
+            :label="$t('appName')"
+            :hint="$t('configNameHint')"
+            v-model="key"
+          ></v-text-field>
+
+          <v-subheader class="pa-0 mt-3">{{$t('ruleContent')}}</v-subheader>
+          <ace-editor lang="properties" v-model="rule" :readonly="readonly"></ace-editor>
+
+        </v-card-text>
+        <v-card-actions>
+          <v-spacer></v-spacer>
+          <v-btn flat @click.native="closeDialog">{{$t('close')}}</v-btn>
+          <v-btn depressed color="primary" @click.native="saveItem">{{$t('save')}}</v-btn>
+        </v-card-actions>
+      </v-card>
+    </v-dialog>
+
+    <v-dialog v-model="warn.display" persistent max-width="500px">
+      <v-card>
+        <v-card-title class="headline">{{$t(this.warn.title) + this.warnStatus.id}}</v-card-title>
+        <v-card-text >{{this.warn.text}}</v-card-text>
+        <v-card-actions>
+          <v-spacer></v-spacer>
+          <v-btn flat @click.native="closeWarn">{{$t('cancel')}}</v-btn>
+          <v-btn depressed color="primary" @click.native="deleteItem(warnStatus)">{{$t('confirm')}}</v-btn>
+        </v-card-actions>
+      </v-card>
+    </v-dialog>
+
+  </v-container>
+</template>
+
+<script>
+    import AceEditor from '@/components/public/AceEditor'
+    import Search from '@/components/public/Search'
+    export default {
+      name: 'Management',
+      components: {
+        AceEditor,
+        Search
+      },
+      data: () => ({
+        configCenter: '',
+        rule: '',
+        updateId: '',
+        key: '',
+        filter: '',
+        readonly: false,
+        dialog: false,
+        operations: [
+          {id: 0, icon: 'visibility', tooltip: 'view'},
+          {id: 1, icon: 'edit', tooltip: 'edit'},
+          {id: 3, icon: 'delete', tooltip: 'delete'}
+        ],
+        warn: {
+          display: false,
+          title: '',
+          text: '',
+          status: {}
+        },
+        warnStatus: {},
+        dubboConfig: [],
+        headers: []
+      }),
+      methods: {
+        setHeaders () {
+          this.headers = [
+            {
+              text: this.$t('name'),
+              value: 'name',
+              align: 'left'
+            },
+            {
+              text: this.$t('scope'),
+              value: 'scope',
+              sortable: false
+            },
+            {
+              text: this.$t('operation'),
+              value: 'operation',
+              sortable: false,
+              width: '115px'
+            }
+          ]
+        },
+        itemOperation (icon, item) {
+          switch (icon) {
+            case 'visibility':
+              this.dialog = true
+              this.rule = item.config
+              this.key = item.key
+              this.readonly = true
+              this.updateId = 'close'
+              break
+            case 'edit':
+              this.dialog = true
+              this.rule = item.config
+              this.key = item.key
+              this.updateId = item.key
+              this.readonly = false
+              break
+            case 'delete':
+              this.openWarn('warnDeleteConfig')
+              this.warnStatus.id = item.key
+          }
+        },
+        deleteItem: function (warnStatus) {
+          this.$axios.delete('/manage/config/' + warnStatus.id)
+            .then(response => {
+              if (response.status === 200) {
+                this.warn.display = false
+                this.search(this.filter)
+                this.$notify.success('Delete success')
+              }
+            })
+        },
+        closeDialog: function () {
+          this.rule = ''
+          this.key = ''
+          this.dialog = false
+          this.readonly = false
+        },
+        openDialog: function () {
+          this.dialog = true
+        },
+        openWarn: function (title, text) {
+          this.warn.title = title
+          this.warn.text = text
+          this.warn.display = true
+        },
+        closeWarn: function () {
+          this.warn.title = ''
+          this.warn.text = ''
+          this.warn.display = false
+        },
+        saveItem: function () {
+          let configDTO = {}
+          if (!this.key) {
+            this.$notify.error('Config key is needed')
+            return
+          }
+          configDTO.key = this.key
+          configDTO.config = this.rule
+          let vm = this
+          if (this.updateId) {
+            if (this.updateId === 'close') {
+              this.closeDialog()
+            } else {
+              this.$axios.put('/manage/config/' + this.updateId, configDTO)
+                .then(response => {
+                  if (response.status === 200) {
+                    vm.search(vm.key)
+                    vm.filter = vm.key
+                    this.closeDialog()
+                    this.$notify.success('Update success')
+                  }
+                })
+            }
+          } else {
+            this.$axios.post('/manage/config/', configDTO)
+              .then(response => {
+                if (response.status === 201) {
+                  vm.search(vm.key)
+                  vm.filter = vm.key
+                  vm.closeDialog()
+                  vm.$notify.success('Create success')
+                }
+              })
+          }
+        },
+        getColor (scope) {
+          if (scope === 'global') {
+            return 'red'
+          }
+          if (scope === 'application') {
+            return 'green'
+          }
+          if (scope === 'service') {
+            return 'blue'
+          }
+        },
+        submit () {
+          this.filter = this.filter.trim()
+          this.search(this.filter)
+        },
+        search (filter) {
+          this.$axios.get('/manage/config/' + filter)
+            .then(response => {
+              if (response.status === 200) {
+                this.dubboConfig = response.data
+                this.$router.push({path: 'management', query: {key: filter}})
+              }
+            })
+        }
+      },
+      mounted () {
+        this.setHeaders()
+        let query = this.$route.query
+        let filter = null
+        Object.keys(query).forEach(function (key) {
+          if (key === 'key') {
+            filter = query[key]
+          }
+        })
+        if (filter !== null) {
+          this.filter = filter
+        } else {
+          this.filter = 'global'
+        }
+        this.search(this.filter)
+      }
+    }
+</script>
+
+<style scoped>
+
+</style>
diff --git a/dubbo-admin-frontend/src/components/ServiceSearch.vue b/dubbo-admin-frontend/src/components/ServiceSearch.vue
index 293c6ce..6d42be3 100644
--- a/dubbo-admin-frontend/src/components/ServiceSearch.vue
+++ b/dubbo-admin-frontend/src/components/ServiceSearch.vue
@@ -312,18 +312,19 @@
         pattern = 'service'
         this.search(this.filter, pattern, true)
       }
-      this.$axios.get('/service', {
-        params: {
-          pattern: 'service',
-          filter: '*'
-        }
-      }).then(response => {
-        let length = response.data.length
-        for (let i = 0; i < length; i++) {
-          vm.serviceItem.push(response.data[i].service)
-          vm.appItem.push(response.data[i].appName)
-        }
-      })
+      this.$axios.get('/services')
+        .then(response => {
+          if (response.status === 200) {
+            vm.serviceItem = response.data
+          }
+        })
+
+      this.$axios.get('/applications')
+        .then(response => {
+          if (response.status === 200) {
+            vm.appItem = response.data
+          }
+        })
     }
 
   }
diff --git a/dubbo-admin-frontend/src/components/governance/TagRule.vue b/dubbo-admin-frontend/src/components/governance/TagRule.vue
index e776576..dbda2f6 100644
--- a/dubbo-admin-frontend/src/components/governance/TagRule.vue
+++ b/dubbo-admin-frontend/src/components/governance/TagRule.vue
@@ -78,14 +78,14 @@
       </v-card>
     </v-dialog>
 
-    <v-dialog v-model="warn" persistent max-width="500px">
+    <v-dialog v-model="warn.display" persistent max-width="500px">
       <v-card>
-        <v-card-title class="headline">{{$t(this.warnTitle)}}</v-card-title>
-        <v-card-text >{{this.warnText}}</v-card-text>
+        <v-card-title class="headline">{{$t(this.warn.title)}}</v-card-title>
+        <v-card-text >{{this.warn.text}}</v-card-text>
         <v-card-actions>
           <v-spacer></v-spacer>
           <v-btn flat @click.native="closeWarn">CANCLE</v-btn>
-          <v-btn depressed color="primary" @click.native="deleteItem(warnStatus)">CONFIRM</v-btn>
+          <v-btn depressed color="primary" @click.native="deleteItem(warn.status)">{{$t('confirm')}}</v-btn>
         </v-card-actions>
       </v-card>
     </v-dialog>
@@ -110,12 +110,14 @@
       pattern: 'Service',
       filter: '',
       dialog: false,
-      warn: false,
       updateId: '',
       application: '',
-      warnTitle: '',
-      warnText: '',
-      warnStatus: {},
+      warn: {
+        display: false,
+        title: '',
+        text: '',
+        status: {}
+      },
       height: 0,
       operations: operations,
       tagRoutingRules: [
@@ -179,14 +181,14 @@
         this.dialog = true
       },
       openWarn: function (title, text) {
-        this.warnTitle = title
-        this.warnText = text
-        this.warn = true
+        this.warn.title = title
+        this.warn.text = text
+        this.warn.display = true
       },
       closeWarn: function () {
-        this.warnTitle = ''
-        this.warnText = ''
-        this.warn = false
+        this.warn.title = ''
+        this.warn.text = ''
+        this.warn.display = false
       },
       saveItem: function () {
         let rule = yaml.safeLoad(this.ruleText)
@@ -248,18 +250,18 @@
             break
           case 'block':
             this.openWarn(' Are you sure to block Tag Rule', 'application: ' + item.application)
-            this.warnStatus.operation = 'disable'
-            this.warnStatus.id = itemId
+            this.warn.status.operation = 'disable'
+            this.warn.status.id = itemId
             break
           case 'check_circle_outline':
             this.openWarn(' Are you sure to enable Tag Rule', 'application: ' + item.application)
-            this.warnStatus.operation = 'enable'
-            this.warnStatus.id = itemId
+            this.warn.status.operation = 'enable'
+            this.warn.status.id = itemId
             break
           case 'delete':
             this.openWarn(' Are you sure to Delete Tag Rule', 'application: ' + item.application)
-            this.warnStatus.operation = 'delete'
-            this.warnStatus.id = itemId
+            this.warn.status.operation = 'delete'
+            this.warn.status.id = itemId
         }
       },
       handleBalance: function (tagRoute, readonly) {
@@ -284,7 +286,7 @@
           this.$axios.delete('/rules/route/tag/' + id)
             .then(response => {
               if (response.status === 200) {
-                this.warn = false
+                this.warn.display = false
                 this.search(this.filter, false)
                 this.$notify.success('Delete success')
               }
@@ -293,7 +295,7 @@
           this.$axios.put('/rules/route/tag/disable/' + id)
             .then(response => {
               if (response.status === 200) {
-                this.warn = false
+                this.warn.display = false
                 this.search(this.filter, false)
                 this.$notify.success('Disable success')
               }
@@ -302,7 +304,7 @@
           this.$axios.put('/rules/route/tag/enable/' + id)
             .then(response => {
               if (response.status === 200) {
-                this.warn = false
+                this.warn.display = false
                 this.search(this.filter, false)
                 this.$notify.success('Enable success')
               }
diff --git a/dubbo-admin-frontend/src/components/public/Search.vue b/dubbo-admin-frontend/src/components/public/Search.vue
index 6ace1e9..15e3432 100644
--- a/dubbo-admin-frontend/src/components/public/Search.vue
+++ b/dubbo-admin-frontend/src/components/public/Search.vue
@@ -21,6 +21,7 @@
       <v-layout row wrap >
         <v-text-field
           :label="label" clearable
+          :hint="hint"
           v-bind:value="value"
           v-on:input="$emit('input', $event)"></v-text-field>
         <v-btn @click="submit" color="primary" large>{{$t('search')}}</v-btn>
@@ -41,6 +42,10 @@
       label: {
         type: String,
         default: ''
+      },
+      hint: {
+        type: String,
+        default: ''
       }
     },
     data: () => ({
diff --git a/dubbo-admin-frontend/src/lang/en.js b/dubbo-admin-frontend/src/lang/en.js
index 593206c..2b771d7 100644
--- a/dubbo-admin-frontend/src/lang/en.js
+++ b/dubbo-admin-frontend/src/lang/en.js
@@ -114,5 +114,13 @@
       rowsPerPageText: 'Rows per page:'
     },
     noDataText: 'No data available'
-  }
+  },
+  configManage: 'Configuration Management',
+  configCenterAddress: 'ConfigCenter Address',
+  searchDubboConfig: 'Search Dubbo Config',
+  createNewDubboConfig: 'Create New Dubbo Config',
+  scope: 'Scope',
+  name: 'Name',
+  warnDeleteConfig: ' Are you sure to Delete Dubbo Config: ',
+  configNameHint: "Application name the config belongs to, use 'global'(without quotes) for global config"
 }
diff --git a/dubbo-admin-frontend/src/lang/zh.js b/dubbo-admin-frontend/src/lang/zh.js
index fc28529..16517d8 100644
--- a/dubbo-admin-frontend/src/lang/zh.js
+++ b/dubbo-admin-frontend/src/lang/zh.js
@@ -114,5 +114,13 @@
       rowsPerPageText: '每页行数:'
     },
     noDataText: '无可用数据'
-  }
+  },
+  configManage: '配置管理',
+  configCenterAddress: '配置中心地址',
+  searchDubboConfig: '搜索Dubbo配置',
+  createNewDubboConfig: '新建Dubbo配置',
+  scope: '范围',
+  name: '名称',
+  warnDeleteConfig: ' 是否要删除Dubbo配置: ',
+  configNameHint: '配置所属的应用名, global 表示全局配置'
 }
diff --git a/dubbo-admin-frontend/src/router/index.js b/dubbo-admin-frontend/src/router/index.js
index adfde47..35f8a90 100644
--- a/dubbo-admin-frontend/src/router/index.js
+++ b/dubbo-admin-frontend/src/router/index.js
@@ -29,6 +29,7 @@
 import ServiceTest from '@/components/test/ServiceTest'
 import ServiceMock from '@/components/test/ServiceMock'
 import ServiceMetrics from '@/components/metrics/ServiceMetrics'
+import Management from '@/components/Management'
 
 Vue.use(Router)
 
@@ -92,6 +93,11 @@
       path: '/metrics',
       name: 'ServiceMetrics',
       component: ServiceMetrics
+    },
+    {
+      path: '/management',
+      name: 'Management',
+      component: Management
     }
 
   ]
diff --git a/pom.xml b/pom.xml
index ee4d1b0..c858050 100644
--- a/pom.xml
+++ b/pom.xml
@@ -58,6 +58,7 @@
         <commons-lang3-version>3.7</commons-lang3-version>
 		<dubbo-version>2.7.0-SNAPSHOT</dubbo-version>
 		<curator-version>2.12.0</curator-version>
+		<curator-test-version>2.12.0</curator-test-version>
 		<fastjson-version>1.2.46</fastjson-version>
 		<springfox-swagger-version>2.9.2</springfox-swagger-version>
 		<netty-version>4.1.30.Final</netty-version>
@@ -102,6 +103,13 @@
 				<artifactId>curator-framework</artifactId>
 				<version>${curator-version}</version>
 			</dependency>
+
+			<dependency>
+				<groupId>org.apache.curator</groupId>
+				<artifactId>curator-test</artifactId>
+				<version>${curator-test-version}</version>
+			</dependency>
+
 			<dependency>
 				<groupId>org.apache.curator</groupId>
 				<artifactId>curator-recipes</artifactId>