You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@griffin.apache.org by gu...@apache.org on 2018/01/15 06:00:51 UTC

incubator-griffin git commit: modify login and get metrics process

Repository: incubator-griffin
Updated Branches:
  refs/heads/master fad7daf7e -> e704da627


modify login and get metrics process

1. simplify login process, with login strategy "default", any user can login regardless of username and password
2. modify returned value of api about metrics to align with UI

Author: He Wang <wa...@qq.com>

Closes #188 from whhe/master.


Project: http://git-wip-us.apache.org/repos/asf/incubator-griffin/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-griffin/commit/e704da62
Tree: http://git-wip-us.apache.org/repos/asf/incubator-griffin/tree/e704da62
Diff: http://git-wip-us.apache.org/repos/asf/incubator-griffin/diff/e704da62

Branch: refs/heads/master
Commit: e704da6270b580df10c1e8ee747a1e4c14eb86de
Parents: fad7daf
Author: He Wang <wa...@qq.com>
Authored: Mon Jan 15 14:00:44 2018 +0800
Committer: Lionel Liu <bh...@163.com>
Committed: Mon Jan 15 14:00:44 2018 +0800

----------------------------------------------------------------------
 .../apache/griffin/core/config/LoginConfig.java |  56 +++++++
 .../griffin/core/login/LoginController.java     |   7 -
 .../apache/griffin/core/login/LoginService.java |   6 -
 .../core/login/LoginServiceDefaultImpl.java     |  44 ++++++
 .../griffin/core/login/LoginServiceImpl.java    | 157 -------------------
 .../core/login/LoginServiceLdapImpl.java        | 107 +++++++++++++
 .../griffin/core/metric/MetricController.java   |   7 +-
 .../griffin/core/metric/MetricService.java      |   3 +-
 .../griffin/core/metric/MetricServiceImpl.java  |  57 +++++--
 .../griffin/core/metric/MetricStoreImpl.java    |  26 ++-
 .../griffin/core/metric/model/Metric.java       |  22 +--
 .../core/util/GriffinOperationMessage.java      |   6 +-
 .../src/main/resources/application.properties   |  17 +-
 13 files changed, 286 insertions(+), 229 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/e704da62/service/src/main/java/org/apache/griffin/core/config/LoginConfig.java
----------------------------------------------------------------------
diff --git a/service/src/main/java/org/apache/griffin/core/config/LoginConfig.java b/service/src/main/java/org/apache/griffin/core/config/LoginConfig.java
new file mode 100644
index 0000000..57066a8
--- /dev/null
+++ b/service/src/main/java/org/apache/griffin/core/config/LoginConfig.java
@@ -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.
+*/
+
+package org.apache.griffin.core.config;
+
+import org.apache.griffin.core.login.LoginService;
+import org.apache.griffin.core.login.LoginServiceDefaultImpl;
+import org.apache.griffin.core.login.LoginServiceLdapImpl;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class LoginConfig {
+
+    @Value("${login.strategy}")
+    private String strategy;
+    @Value("${ldap.url}")
+    private String url;
+    @Value("${ldap.email}")
+    private String email;
+    @Value("${ldap.searchBase}")
+    private String searchBase;
+    @Value("${ldap.searchPattern}")
+    private String searchPattern;
+
+
+    @Bean
+    public LoginService loginService() {
+        switch (strategy) {
+            case "default":
+                return new LoginServiceDefaultImpl();
+            case "ldap":
+                return new LoginServiceLdapImpl(url, email, searchBase, searchPattern);
+            default:
+                return null;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/e704da62/service/src/main/java/org/apache/griffin/core/login/LoginController.java
----------------------------------------------------------------------
diff --git a/service/src/main/java/org/apache/griffin/core/login/LoginController.java b/service/src/main/java/org/apache/griffin/core/login/LoginController.java
index 511f59e..a4e997d 100644
--- a/service/src/main/java/org/apache/griffin/core/login/LoginController.java
+++ b/service/src/main/java/org/apache/griffin/core/login/LoginController.java
@@ -19,10 +19,7 @@ under the License.
 
 package org.apache.griffin.core.login;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.core.env.Environment;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -34,14 +31,10 @@ import java.util.Map;
 @RestController
 @RequestMapping("/api/v1/login")
 public class LoginController {
-    private static final Logger LOGGER = LoggerFactory.getLogger(LoginController.class);
 
     @Autowired
     private LoginService loginService;
 
-    @Autowired
-    private Environment env;
-
     @RequestMapping(value = "/authenticate", method = RequestMethod.POST)
     public ResponseEntity<Map<String, Object>> login(
             @RequestBody Map<String, String> map) {

http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/e704da62/service/src/main/java/org/apache/griffin/core/login/LoginService.java
----------------------------------------------------------------------
diff --git a/service/src/main/java/org/apache/griffin/core/login/LoginService.java b/service/src/main/java/org/apache/griffin/core/login/LoginService.java
index bdb5a64..6457804 100644
--- a/service/src/main/java/org/apache/griffin/core/login/LoginService.java
+++ b/service/src/main/java/org/apache/griffin/core/login/LoginService.java
@@ -26,10 +26,4 @@ import java.util.Map;
 public interface LoginService {
 
     ResponseEntity<Map<String, Object>> login(Map<String, String> map);
-
-    ResponseEntity<Map<String, Object>> loginDefault(Map<String, String> map);
-
-    ResponseEntity<Map<String, Object>> loginLDAP(Map<String, String> map);
-
-
 }

http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/e704da62/service/src/main/java/org/apache/griffin/core/login/LoginServiceDefaultImpl.java
----------------------------------------------------------------------
diff --git a/service/src/main/java/org/apache/griffin/core/login/LoginServiceDefaultImpl.java b/service/src/main/java/org/apache/griffin/core/login/LoginServiceDefaultImpl.java
new file mode 100644
index 0000000..68e6650
--- /dev/null
+++ b/service/src/main/java/org/apache/griffin/core/login/LoginServiceDefaultImpl.java
@@ -0,0 +1,44 @@
+/*
+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.griffin.core.login;
+
+import org.apache.commons.lang.StringUtils;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class LoginServiceDefaultImpl implements LoginService {
+
+    @Override
+    public ResponseEntity<Map<String, Object>> login(Map<String, String> map) {
+        String username = map.get("username");
+        if (StringUtils.isBlank(username)) {
+            username = "Anonymous";
+        }
+        String fullName = username;
+        Map<String, Object> message = new HashMap<>();
+        message.put("ntAccount", username);
+        message.put("fullName", fullName);
+        message.put("status", 0);
+        return new ResponseEntity<>(message, HttpStatus.OK);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/e704da62/service/src/main/java/org/apache/griffin/core/login/LoginServiceImpl.java
----------------------------------------------------------------------
diff --git a/service/src/main/java/org/apache/griffin/core/login/LoginServiceImpl.java b/service/src/main/java/org/apache/griffin/core/login/LoginServiceImpl.java
deleted file mode 100644
index 5f8a069..0000000
--- a/service/src/main/java/org/apache/griffin/core/login/LoginServiceImpl.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
-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.griffin.core.login;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.core.env.Environment;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.stereotype.Service;
-
-import javax.naming.Context;
-import javax.naming.NamingEnumeration;
-import javax.naming.NamingException;
-import javax.naming.directory.Attributes;
-import javax.naming.directory.SearchControls;
-import javax.naming.directory.SearchResult;
-import javax.naming.ldap.InitialLdapContext;
-import javax.naming.ldap.LdapContext;
-import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.Map;
-
-@Service
-public class LoginServiceImpl implements LoginService {
-    private static final Logger LOGGER = LoggerFactory.getLogger(LoginServiceImpl.class);
-
-    @Autowired
-    private Environment env;
-
-    @Override
-    public ResponseEntity<Map<String, Object>> login(Map<String, String> map) {
-        String strategy = env.getProperty("login.strategy");
-        switch (strategy) {
-            case "ldap":
-                return loginLDAP(map);
-            case "default":
-                return loginDefault(map);
-            default: {
-                LOGGER.error("Missing login strategy configuration");
-                return new ResponseEntity<Map<String, Object>>(new HashMap<String, Object>(), HttpStatus.NOT_FOUND);
-            }
-        }
-    }
-
-    @Override
-    public ResponseEntity<Map<String, Object>> loginDefault(Map<String, String> map) {
-        String username = map.get("username");
-        String password = map.get("password");
-        if (username == null || password == null) {
-            LOGGER.error("Missing default login input");
-            return null;
-        }
-        String fullName = null;
-        if (username.equals("user")) {
-            if (password.equals("test")) {
-                fullName = "Default";
-            }
-        }
-        return getResponse(username, fullName);
-    }
-
-    @Override
-    public ResponseEntity<Map<String, Object>> loginLDAP(Map<String, String> map) {
-        String ntAccount = map.get("username");
-        String password = map.get("password");
-        if (ntAccount == null || password == null) {
-            LOGGER.error("Missing ldap login input");
-            return null;
-        }
-        String fullName = searchLDAP(ntAccount, password);
-        return getResponse(ntAccount, fullName);
-    }
-
-    private String searchLDAP(String ntAccount, String password) {
-        String domainComponent = env.getProperty("ldap.dc");
-        Hashtable<String, String> ht = getLDAPEnvironmrnt(ntAccount, password);
-        if (domainComponent == null || ht == null) {
-            return null;
-        }
-        LdapContext ctx;
-        try {
-            String searchFilter = "(sAMAccountName=" + ntAccount + ")";
-            SearchControls searchControls = new SearchControls();
-            searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
-            ctx = new InitialLdapContext(ht, null);
-            NamingEnumeration<SearchResult> results = ctx.search(domainComponent, searchFilter, searchControls);
-            String fullName = ntAccount;
-            SearchResult searchResult = null;
-            while (results.hasMoreElements()) {
-                searchResult = results.nextElement();
-                Attributes attrs = searchResult.getAttributes();
-                if (attrs != null && attrs.get("cn") != null) {
-                    String cnName = (String) attrs.get("cn").get();
-                    if (cnName.indexOf("(") > 0) {
-                        fullName = cnName.substring(0, cnName.indexOf("("));
-                    }
-                }
-            }
-            return fullName;
-        } catch (NamingException e) {
-            LOGGER.info("Failed to login with LDAP auth");
-        }
-        return null;
-    }
-
-    private Hashtable<String, String> getLDAPEnvironmrnt(String ntAccount, String password) {
-        String ldapUrl = env.getProperty("ldap.url");
-        String domain = env.getProperty("ldap.domain");
-        String connectTimeout = env.getProperty("ldap.connect-timeout");
-        String readTimeout = env.getProperty("ldap.read-timeout");
-        if (ldapUrl == null || domain == null || connectTimeout == null || readTimeout == null) {
-            LOGGER.error("Missing ldap properties");
-            return null;
-        }
-        String ldapUser = ntAccount + "@" + domain;
-        String ldapFactory = "com.sun.jndi.ldap.LdapCtxFactory";
-        Hashtable<String, String> ht = new Hashtable<String, String>();
-        ht.put(Context.INITIAL_CONTEXT_FACTORY, ldapFactory);
-        ht.put("com.sun.jndi.ldap.connect.timeout", connectTimeout);
-        ht.put("com.sun.jndi.ldap.read.timeout", readTimeout);
-        ht.put(Context.PROVIDER_URL, ldapUrl);
-        ht.put(Context.SECURITY_PRINCIPAL, ldapUser);
-        ht.put(Context.SECURITY_CREDENTIALS, password);
-        return ht;
-    }
-
-    private ResponseEntity<Map<String, Object>> getResponse(String ntAccount, String fullName) {
-        Map<String, Object> message = new HashMap<String, Object>();
-        if (fullName != null) {
-            message.put("ntAccount", ntAccount);
-            message.put("fullName", fullName);
-            message.put("status", 0);
-            return new ResponseEntity<Map<String, Object>>(message, HttpStatus.OK);
-        } else {
-            return new ResponseEntity<Map<String, Object>>(message, HttpStatus.NOT_FOUND);
-        }
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/e704da62/service/src/main/java/org/apache/griffin/core/login/LoginServiceLdapImpl.java
----------------------------------------------------------------------
diff --git a/service/src/main/java/org/apache/griffin/core/login/LoginServiceLdapImpl.java b/service/src/main/java/org/apache/griffin/core/login/LoginServiceLdapImpl.java
new file mode 100644
index 0000000..270f5d1
--- /dev/null
+++ b/service/src/main/java/org/apache/griffin/core/login/LoginServiceLdapImpl.java
@@ -0,0 +1,107 @@
+/*
+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.griffin.core.login;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+public class LoginServiceLdapImpl implements LoginService {
+    private static final Logger LOGGER = LoggerFactory.getLogger(LoginServiceLdapImpl.class);
+
+    private static final String LDAP_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";
+
+    private String url;
+    private String email;
+    private String searchBase;
+    private String searchPattern;
+    private SearchControls searchControls;
+
+    public LoginServiceLdapImpl(String url, String email, String searchBase, String searchPattern) {
+        this.url = url;
+        this.email = email;
+        this.searchBase = searchBase;
+        this.searchPattern = searchPattern;
+        SearchControls searchControls = new SearchControls();
+        searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+        this.searchControls = searchControls;
+    }
+
+    @Override
+    public ResponseEntity<Map<String, Object>> login(Map<String, String> map) {
+        String ntAccount = map.get("username");
+        String password = map.get("password");
+        String searchFilter = searchPattern.replace("{0}", ntAccount);
+        try {
+            LdapContext ctx = getContextInstance(ntAccount, password);
+            NamingEnumeration<SearchResult> results = ctx.search(searchBase, searchFilter, searchControls);
+            String fullName = getFullName(results, ntAccount);
+            Map<String, Object> message = new HashMap<>();
+            message.put("ntAccount", ntAccount);
+            message.put("fullName", fullName);
+            message.put("status", 0);
+            return new ResponseEntity<>(message, HttpStatus.OK);
+        } catch (NamingException e) {
+            LOGGER.warn("User {} failed to login with LDAP auth. {}", ntAccount, e.getMessage());
+        }
+        return null;
+    }
+
+    private String getFullName(NamingEnumeration<SearchResult> results, String ntAccount) {
+        String fullName = ntAccount;
+        try {
+            while (results.hasMoreElements()) {
+                SearchResult searchResult = results.nextElement();
+                Attributes attrs = searchResult.getAttributes();
+                if (attrs != null && attrs.get("cn") != null) {
+                    String cnName = (String) attrs.get("cn").get();
+                    if (cnName.indexOf("(") > 0) {
+                        fullName = cnName.substring(0, cnName.indexOf("("));
+                    }
+                }
+            }
+        } catch (NamingException e) {
+            LOGGER.warn("User {} successfully login with LDAP auth, but failed to get full name.", ntAccount);
+        }
+        return fullName;
+    }
+
+    private LdapContext getContextInstance(String ntAccount, String password) throws NamingException {
+        Hashtable<String, String> ht = new Hashtable<>();
+        ht.put(Context.INITIAL_CONTEXT_FACTORY, LDAP_FACTORY);
+        ht.put(Context.PROVIDER_URL, url);
+        ht.put(Context.SECURITY_PRINCIPAL, ntAccount + email);
+        ht.put(Context.SECURITY_CREDENTIALS, password);
+        return new InitialLdapContext(ht, null);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/e704da62/service/src/main/java/org/apache/griffin/core/metric/MetricController.java
----------------------------------------------------------------------
diff --git a/service/src/main/java/org/apache/griffin/core/metric/MetricController.java b/service/src/main/java/org/apache/griffin/core/metric/MetricController.java
index 981454c..fcaa51d 100644
--- a/service/src/main/java/org/apache/griffin/core/metric/MetricController.java
+++ b/service/src/main/java/org/apache/griffin/core/metric/MetricController.java
@@ -26,10 +26,7 @@ import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.List;
-
-/**
- * In griffin, metricName usually equals to measureName, and we only save measureName in server.
- */
+import java.util.Map;
 
 @RestController
 @RequestMapping("/api/v1")
@@ -39,7 +36,7 @@ public class MetricController {
     private MetricService metricService;
 
     @RequestMapping(value = "/metrics", method = RequestMethod.GET)
-    public List<Metric> getAllMetrics() {
+    public Map<String, List<Metric>> getAllMetrics() {
         return metricService.getAllMetrics();
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/e704da62/service/src/main/java/org/apache/griffin/core/metric/MetricService.java
----------------------------------------------------------------------
diff --git a/service/src/main/java/org/apache/griffin/core/metric/MetricService.java b/service/src/main/java/org/apache/griffin/core/metric/MetricService.java
index 6b00f1b..f68d83c 100644
--- a/service/src/main/java/org/apache/griffin/core/metric/MetricService.java
+++ b/service/src/main/java/org/apache/griffin/core/metric/MetricService.java
@@ -25,10 +25,11 @@ import org.apache.griffin.core.metric.model.MetricValue;
 import org.springframework.http.ResponseEntity;
 
 import java.util.List;
+import java.util.Map;
 
 public interface MetricService {
 
-    List<Metric> getAllMetrics();
+    Map<String, List<Metric>> getAllMetrics();
 
     List<MetricValue> getMetricValues(String metricName, int offset, int size);
 

http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/e704da62/service/src/main/java/org/apache/griffin/core/metric/MetricServiceImpl.java
----------------------------------------------------------------------
diff --git a/service/src/main/java/org/apache/griffin/core/metric/MetricServiceImpl.java b/service/src/main/java/org/apache/griffin/core/metric/MetricServiceImpl.java
index 1501f94..68b26eb 100644
--- a/service/src/main/java/org/apache/griffin/core/metric/MetricServiceImpl.java
+++ b/service/src/main/java/org/apache/griffin/core/metric/MetricServiceImpl.java
@@ -20,12 +20,16 @@ under the License.
 package org.apache.griffin.core.metric;
 
 
+import org.apache.commons.collections.MapUtils;
+import org.apache.commons.lang.StringUtils;
 import org.apache.griffin.core.job.entity.AbstractJob;
 import org.apache.griffin.core.job.repo.JobRepo;
 import org.apache.griffin.core.measure.entity.Measure;
 import org.apache.griffin.core.measure.repo.MeasureRepo;
 import org.apache.griffin.core.metric.model.Metric;
 import org.apache.griffin.core.metric.model.MetricValue;
+import org.apache.griffin.core.util.GriffinOperationMessage;
+import org.elasticsearch.client.ResponseException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -33,10 +37,7 @@ import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Service;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
@@ -52,17 +53,25 @@ public class MetricServiceImpl implements MetricService {
     private MetricStore metricStore;
 
     @Override
-    public List<Metric> getAllMetrics() {
-        List<Metric> metrics = new ArrayList<>();
+    public Map<String, List<Metric>> getAllMetrics() {
+        Map<String, List<Metric>> metricMap = new HashMap<>();
         List<AbstractJob> jobs = jobRepo.findByDeleted(false);
         List<Measure> measures = measureRepo.findByDeleted(false);
         Map<Long, Measure> measureMap = measures.stream().collect(Collectors.toMap(Measure::getId, Function.identity()));
-        for (AbstractJob job : jobs) {
-            List<MetricValue> metricValues = getMetricValues(job.getMetricName(), 0, 300);
-            Measure measure = measureMap.get(job.getMeasureId());
-            metrics.add(new Metric(job.getJobName(), measure.getDescription(), measure.getOrganization(), measure.getOwner(), metricValues));
+        Map<Long, List<AbstractJob>> jobMap = jobs.stream().collect(Collectors.groupingBy(AbstractJob::getMeasureId, Collectors.toList()));
+        for (Map.Entry<Long, List<AbstractJob>> entry : jobMap.entrySet()) {
+            Long measureId = entry.getKey();
+            Measure measure = measureMap.get(measureId);
+            List<AbstractJob> jobList = entry.getValue();
+            List<Metric> metrics = new ArrayList<>();
+            for (AbstractJob job : jobList) {
+                List<MetricValue> metricValues = getMetricValues(job.getMetricName(), 0, 300);
+                metrics.add(new Metric(job.getMetricName(), measure.getOwner(), metricValues));
+            }
+            metricMap.put(measure.getName(), metrics);
+
         }
-        return metrics;
+        return metricMap;
     }
 
     @Override
@@ -77,25 +86,43 @@ public class MetricServiceImpl implements MetricService {
 
     @Override
     public ResponseEntity addMetricValues(List<MetricValue> values) {
+        for (MetricValue value : values) {
+            if (!isMetricValueValid(value)) {
+                LOGGER.error("Invalid metric value.");
+                return new ResponseEntity<>(GriffinOperationMessage.ADD_METRIC_VALUES_FAIL, HttpStatus.BAD_REQUEST);
+            }
+        }
         try {
             for (MetricValue value : values) {
                 metricStore.addMetricValue(value);
             }
-            return new ResponseEntity("Add Metric Values Success", HttpStatus.CREATED);
+            return new ResponseEntity<>(GriffinOperationMessage.ADD_METRIC_VALUES_SUCCESS, HttpStatus.CREATED);
+        } catch (ResponseException e) {
+            LOGGER.error("Failed to add metric values. {}", e.getMessage());
+            HttpStatus status = HttpStatus.valueOf(e.getResponse().getStatusLine().getStatusCode());
+            return new ResponseEntity<>(GriffinOperationMessage.ADD_METRIC_VALUES_FAIL, status);
         } catch (Exception e) {
             LOGGER.error("Failed to add metric values. {}", e.getMessage());
-            return new ResponseEntity("Add Metric Values Failed", HttpStatus.INTERNAL_SERVER_ERROR);
+            return new ResponseEntity<>(GriffinOperationMessage.ADD_METRIC_VALUES_FAIL, HttpStatus.INTERNAL_SERVER_ERROR);
         }
     }
 
+    private boolean isMetricValueValid(MetricValue value) {
+        return StringUtils.isNotBlank(value.getName()) && value.getTmst() != null && MapUtils.isNotEmpty(value.getValue());
+    }
+
     @Override
     public ResponseEntity deleteMetricValues(String metricName) {
         try {
             metricStore.deleteMetricValues(metricName);
-            return ResponseEntity.ok("Delete Metric Values Success");
+            return ResponseEntity.ok(GriffinOperationMessage.DELETE_METRIC_VALUES_SUCCESS);
+        } catch (ResponseException e) {
+            LOGGER.error("Failed to delete metric values named {}. {}", metricName, e.getMessage());
+            HttpStatus status = HttpStatus.valueOf(e.getResponse().getStatusLine().getStatusCode());
+            return new ResponseEntity<>(GriffinOperationMessage.DELETE_METRIC_VALUES_FAIL, status);
         } catch (Exception e) {
             LOGGER.error("Failed to delete metric values named {}. {}", metricName, e.getMessage());
-            return new ResponseEntity("Delete Metric Values Failed", HttpStatus.INTERNAL_SERVER_ERROR);
+            return new ResponseEntity<>(GriffinOperationMessage.DELETE_METRIC_VALUES_FAIL, HttpStatus.INTERNAL_SERVER_ERROR);
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/e704da62/service/src/main/java/org/apache/griffin/core/metric/MetricStoreImpl.java
----------------------------------------------------------------------
diff --git a/service/src/main/java/org/apache/griffin/core/metric/MetricStoreImpl.java b/service/src/main/java/org/apache/griffin/core/metric/MetricStoreImpl.java
index 1a81aee..30b60f9 100644
--- a/service/src/main/java/org/apache/griffin/core/metric/MetricStoreImpl.java
+++ b/service/src/main/java/org/apache/griffin/core/metric/MetricStoreImpl.java
@@ -19,6 +19,8 @@ under the License.
 
 package org.apache.griffin.core.metric;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import org.apache.griffin.core.metric.model.MetricValue;
@@ -34,6 +36,7 @@ import org.elasticsearch.client.RestClient;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
 
+import java.io.IOException;
 import java.util.*;
 
 @Component
@@ -49,23 +52,32 @@ public class MetricStoreImpl implements MetricStore {
 
     @Override
     public List<MetricValue> getMetricValues(String metricName, int from, int size) throws Exception {
+        HttpEntity entity = getHttpEntity(metricName, from, size);
+        Response response = client.performRequest("GET", "/griffin/accuracy/_search?filter_path=hits.hits._source",
+                Collections.emptyMap(), entity, new BasicHeader("Content-Type", "application/json"));
+        return getMetricValues(response);
+    }
+
+    private HttpEntity getHttpEntity(String metricName, int from, int size) throws JsonProcessingException {
         Map<String, Object> map = new HashMap<>();
-        Map queryParam = Collections.singletonMap("term", Collections.singletonMap("name.keyword", metricName));
-        Map sortParam = Collections.singletonMap("tmst", Collections.singletonMap("order", "desc"));
+        Map<String, Object> queryParam = Collections.singletonMap("term", Collections.singletonMap("name.keyword", metricName));
+        Map<String, Object> sortParam = Collections.singletonMap("tmst", Collections.singletonMap("order", "desc"));
         map.put("query", queryParam);
         map.put("sort", sortParam);
         map.put("from", from);
         map.put("size", size);
+        return new NStringEntity(JsonUtil.toJson(map), ContentType.APPLICATION_JSON);
+    }
+
+    private List<MetricValue> getMetricValues(Response response) throws IOException {
         List<MetricValue> metricValues = new ArrayList<>();
-        HttpEntity entity = new NStringEntity(JsonUtil.toJson(map), ContentType.APPLICATION_JSON);
-        Response response = client.performRequest("GET", "/griffin/accuracy/_search?filter_path=hits.hits._source",
-                Collections.emptyMap(), entity, new BasicHeader("Content-Type", "application/json"));
         JsonNode jsonNode = mapper.readTree(EntityUtils.toString(response.getEntity()));
         if (jsonNode.hasNonNull("hits") && jsonNode.get("hits").hasNonNull("hits")) {
             for (JsonNode node : jsonNode.get("hits").get("hits")) {
                 JsonNode sourceNode = node.get("_source");
                 metricValues.add(new MetricValue(sourceNode.get("name").asText(), Long.parseLong(sourceNode.get("tmst").asText()),
-                        JsonUtil.toEntity(sourceNode.get("value").toString(), Map.class)));
+                        JsonUtil.toEntity(sourceNode.get("value").toString(), new TypeReference<Map<String, Object>>() {
+                        })));
             }
         }
         return metricValues;
@@ -81,7 +93,7 @@ public class MetricStoreImpl implements MetricStore {
 
     @Override
     public void deleteMetricValues(String metricName) throws Exception {
-        Map param = Collections.singletonMap("query",
+        Map<String, Object> param = Collections.singletonMap("query",
                 Collections.singletonMap("term", Collections.singletonMap("name.keyword", metricName)));
         HttpEntity entity = new NStringEntity(JsonUtil.toJson(param), ContentType.APPLICATION_JSON);
         client.performRequest("POST", "/griffin/accuracy/_delete_by_query", Collections.emptyMap(),

http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/e704da62/service/src/main/java/org/apache/griffin/core/metric/model/Metric.java
----------------------------------------------------------------------
diff --git a/service/src/main/java/org/apache/griffin/core/metric/model/Metric.java b/service/src/main/java/org/apache/griffin/core/metric/model/Metric.java
index 517c175..6de7a33 100644
--- a/service/src/main/java/org/apache/griffin/core/metric/model/Metric.java
+++ b/service/src/main/java/org/apache/griffin/core/metric/model/Metric.java
@@ -24,18 +24,14 @@ import java.util.List;
 public class Metric {
 
     private String name;
-    private String description;
-    private String organization;
     private String owner;
     private List<MetricValue> metricValues;
 
     public Metric() {
     }
 
-    public Metric(String name, String description, String organization, String owner, List<MetricValue> metricValues) {
+    public Metric(String name, String owner, List<MetricValue> metricValues) {
         this.name = name;
-        this.description = description;
-        this.organization = organization;
         this.owner = owner;
         this.metricValues = metricValues;
     }
@@ -48,22 +44,6 @@ public class Metric {
         this.name = name;
     }
 
-    public String getDescription() {
-        return description;
-    }
-
-    public void setDescription(String description) {
-        this.description = description;
-    }
-
-    public String getOrganization() {
-        return organization;
-    }
-
-    public void setOrganization(String organization) {
-        this.organization = organization;
-    }
-
     public String getOwner() {
         return owner;
     }

http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/e704da62/service/src/main/java/org/apache/griffin/core/util/GriffinOperationMessage.java
----------------------------------------------------------------------
diff --git a/service/src/main/java/org/apache/griffin/core/util/GriffinOperationMessage.java b/service/src/main/java/org/apache/griffin/core/util/GriffinOperationMessage.java
index 982efb6..86f1aac 100644
--- a/service/src/main/java/org/apache/griffin/core/util/GriffinOperationMessage.java
+++ b/service/src/main/java/org/apache/griffin/core/util/GriffinOperationMessage.java
@@ -34,6 +34,8 @@ public enum GriffinOperationMessage {
     SET_JOB_DELETED_STATUS_SUCCESS(207, "Set Job Deleted Status Succeed"),
     PAUSE_JOB_SUCCESS(208, "Pause Job Succeed"),
     UPDATE_JOB_INSTANCE_SUCCESS(209, "Update Job Instance Succeed"),
+    ADD_METRIC_VALUES_SUCCESS(210, "Add Metric Values Succeed"),
+    DELETE_METRIC_VALUES_SUCCESS(211, "Delete Metric Values Succeed"),
 
     //failed
     RESOURCE_NOT_FOUND(400, "Resource Not Found"),
@@ -47,7 +49,9 @@ public enum GriffinOperationMessage {
     PAUSE_JOB_FAIL(408, "Pause Job Failed"),
     UPDATE_JOB_INSTANCE_FAIL(409, "Update Job Instance Failed"),
     CREATE_MEASURE_FAIL_DUPLICATE(410, "Create Measure Failed, duplicate records"),
-    UNEXPECTED_RUNTIME_EXCEPTION(411, "Unexpected RuntimeException");
+    UNEXPECTED_RUNTIME_EXCEPTION(411, "Unexpected RuntimeException"),
+    ADD_METRIC_VALUES_FAIL(412, "Add Metric Values Failed"),
+    DELETE_METRIC_VALUES_FAIL(413, "Delete Metric Values Failed");
 
     private final int code;
     private final String description;

http://git-wip-us.apache.org/repos/asf/incubator-griffin/blob/e704da62/service/src/main/resources/application.properties
----------------------------------------------------------------------
diff --git a/service/src/main/resources/application.properties b/service/src/main/resources/application.properties
index 3e8d600..f2d83f4 100644
--- a/service/src/main/resources/application.properties
+++ b/service/src/main/resources/application.properties
@@ -22,7 +22,7 @@ spring.datasource.username = griffin
 spring.datasource.password = 123456
 spring.datasource.driver-class-name = com.mysql.jdbc.Driver
 
-# Hibernate ddl auto (validate,create, create-drop, update)
+# Hibernate ddl auto (validate, create, create-drop, update)
 spring.jpa.hibernate.ddl-auto = update
 spring.jpa.show-sql = true
 spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
@@ -53,17 +53,16 @@ predicate.job.repeat.count = 12
 # external properties directory location
 external.config.location =
 
-#login strategy
+# login strategy ("default" or "ldap")
 login.strategy = default
 
-#ldap
-ldap.url=ldap://<ldap url>
-ldap.domain=<account domain>
-ldap.dc=<domain components config>
-ldap.connect-timeout=<connect timeout config>
-ldap.read-timeout=<read timeout config>
+# ldap
+ldap.url = ldap://hostname:port
+ldap.email = @example.com
+ldap.searchBase = DC=org,DC=example
+ldap.searchPattern = (sAMAccountName={0})
 
-#hdfs
+# hdfs
 fs.defaultFS = hdfs://hdfs-default-name
 
 # elasticsearch