This is an automated email from the ASF dual-hosted git repository.

adoroszlai pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/master by this push:
     new f840458efa HDDS-9831. Fix NPE and remove unnecessary logic in 
HddsConfServlet (#5733)
f840458efa is described below

commit f840458efab58b3f692e812d3d24514f85892131
Author: Zhaohui Wang <[email protected]>
AuthorDate: Thu Dec 7 17:55:02 2023 +0800

    HDDS-9831. Fix NPE and remove unnecessary logic in HddsConfServlet (#5733)
---
 .../apache/hadoop/hdds/conf/HddsConfServlet.java   |  16 +-
 .../hadoop/hdds/conf/TestHddsConfServlet.java      | 318 +++++++++++++++++++++
 2 files changed, 325 insertions(+), 9 deletions(-)

diff --git 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/conf/HddsConfServlet.java
 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/conf/HddsConfServlet.java
index e852db0c31..15f56f9d56 100644
--- 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/conf/HddsConfServlet.java
+++ 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/conf/HddsConfServlet.java
@@ -28,6 +28,7 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Properties;
 
+import com.google.common.base.Strings;
 import org.apache.hadoop.hdds.annotation.InterfaceAudience;
 import org.apache.hadoop.hdds.annotation.InterfaceStability;
 import org.apache.hadoop.http.HttpServer2;
@@ -97,15 +98,9 @@ public class HddsConfServlet extends HttpServlet {
       throws IOException {
     try {
       if (cmd == null) {
-        if (FORMAT_XML.equals(format)) {
-          response.setContentType("text/xml; charset=utf-8");
-        } else if (FORMAT_JSON.equals(format)) {
-          response.setContentType("application/json; charset=utf-8");
-        }
-
         writeResponse(getConfFromContext(), out, format, name);
       } else {
-        processConfigTagRequest(request, out);
+        processConfigTagRequest(request, cmd, out);
       }
     } catch (BadFormatException bfe) {
       response.sendError(HttpServletResponse.SC_BAD_REQUEST, bfe.getMessage());
@@ -148,9 +143,8 @@ public class HddsConfServlet extends HttpServlet {
     }
   }
 
-  private void processConfigTagRequest(HttpServletRequest request,
+  private void processConfigTagRequest(HttpServletRequest request, String cmd,
       Writer out) throws IOException {
-    String cmd = request.getParameter(COMMAND);
     Gson gson = new Gson();
     OzoneConfiguration config = getOzoneConfig();
 
@@ -160,6 +154,10 @@ public class HddsConfServlet extends HttpServlet {
       break;
     case "getPropertyByTag":
       String tags = request.getParameter("tags");
+      if (Strings.isNullOrEmpty(tags)) {
+        throw new IllegalArgumentException("The tags parameter should be set" +
+                " when using the getPropertyByTag command.");
+      }
       Map<String, Properties> propMap = new HashMap<>();
 
       for (String tag : tags.split(",")) {
diff --git 
a/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/conf/TestHddsConfServlet.java
 
b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/conf/TestHddsConfServlet.java
new file mode 100644
index 0000000000..5ad7c80fac
--- /dev/null
+++ 
b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/conf/TestHddsConfServlet.java
@@ -0,0 +1,318 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.hadoop.hdds.conf;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import com.google.gson.Gson;
+import org.apache.hadoop.http.HttpServer2;
+import org.apache.hadoop.thirdparty.com.google.common.base.Strings;
+import org.apache.hadoop.util.XMLUtils;
+import org.eclipse.jetty.util.ajax.JSON;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.BeforeAll;
+import org.mockito.Mockito;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.HttpHeaders;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+/** Test for {@link HddsConfServlet}. */
+public class TestHddsConfServlet {
+
+  private static final Map<String, String> TEST_PROPERTIES = new HashMap<>();
+  private static final Map<String, String> TEST_FORMATS = new HashMap<>();
+  private static final String TEST_KEY = "testconfservlet.key";
+  private static final String TEST_VAL = "testval";
+
+  @BeforeAll
+  public static void setup() {
+    TEST_PROPERTIES.put("test.key1", "value1");
+    TEST_PROPERTIES.put("test.key2", "value2");
+    TEST_PROPERTIES.put("test.key3", "value3");
+    TEST_FORMATS.put(HddsConfServlet.FORMAT_XML, "application/xml");
+    TEST_FORMATS.put(HddsConfServlet.FORMAT_JSON, "application/json");
+  }
+
+  @Test
+  public void testParseHeaders() throws Exception {
+    HashMap<String, String> verifyMap = new HashMap<String, String>();
+    verifyMap.put("text/plain", HddsConfServlet.FORMAT_XML);
+    verifyMap.put(null, HddsConfServlet.FORMAT_XML);
+    verifyMap.put("text/xml", HddsConfServlet.FORMAT_XML);
+    verifyMap.put("application/xml", HddsConfServlet.FORMAT_XML);
+    verifyMap.put("application/json", HddsConfServlet.FORMAT_JSON);
+
+    HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
+    for (Map.Entry<String, String> entry : verifyMap.entrySet()) {
+      String contenTypeActual = entry.getValue();
+      Mockito.when(request.getHeader(HttpHeaders.ACCEPT))
+          .thenReturn(entry.getKey());
+      assertEquals(contenTypeActual,
+          HddsConfServlet.parseAcceptHeader(request));
+    }
+  }
+
+  @Test
+  public void testGetProperty() throws Exception {
+    OzoneConfiguration conf = getPropertiesConf();
+    // list various of property names
+    String[] keys = new String[] {"test1.key1",
+        "test.unknown.key",
+        "",
+        "test.key2",
+        null};
+    for (Map.Entry<String, String> entry : TEST_FORMATS.entrySet()) {
+      for (String key : keys) {
+        verifyGetProperty(conf, entry.getKey(), key);
+      }
+    }
+  }
+
+  @Test
+  public void testGetPropertyWithCmd() throws Exception {
+    OzoneConfiguration conf = getPropertiesConf();
+    conf.getObject(OzoneTestConfig.class);
+    // test cmd is getOzoneTags
+    String result = getResultWithCmd(conf, "getOzoneTags");
+    Gson gson = new Gson();
+    String tags = gson.toJson(OzoneConfiguration.TAGS);
+    assertEquals(result, tags);
+    // cmd is getPropertyByTag
+    result = getResultWithCmd(conf, "getPropertyByTag");
+    assertTrue(result.contains("ozone.test.test.key"));
+    // cmd is illegal
+    getResultWithCmd(conf, "illegal");
+  }
+
+  @Test
+  @SuppressWarnings("unchecked")
+  public void testWriteJson() throws Exception {
+    StringWriter sw = new StringWriter();
+    HddsConfServlet.writeResponse(getTestConf(), sw, "json", null);
+    String json = sw.toString();
+    boolean foundSetting = false;
+    Object parsed = JSON.parse(json);
+    Object[] properties = ((Map<String, Object[]>) parsed).get("properties");
+    for (Object o : properties) {
+      Map<String, Object> propertyInfo = (Map<String, Object>) o;
+      String key = (String) propertyInfo.get("key");
+      String val = (String) propertyInfo.get("value");
+      String resource = (String) propertyInfo.get("resource");
+      if (TEST_KEY.equals(key) && TEST_VAL.equals(val)
+          && "programmatically".equals(resource)) {
+        foundSetting = true;
+      }
+    }
+    assertTrue(foundSetting);
+  }
+
+  @Test
+  public void testWriteXml() throws Exception {
+    StringWriter sw = new StringWriter();
+    HddsConfServlet.writeResponse(getTestConf(), sw, "xml", null);
+    String xml = sw.toString();
+
+    DocumentBuilderFactory docBuilderFactory =
+        XMLUtils.newSecureDocumentBuilderFactory();
+    DocumentBuilder builder = docBuilderFactory.newDocumentBuilder();
+    Document doc = builder.parse(new InputSource(new StringReader(xml)));
+    NodeList nameNodes = doc.getElementsByTagName("name");
+    boolean foundSetting = false;
+    for (int i = 0; i < nameNodes.getLength(); i++) {
+      Node nameNode = nameNodes.item(i);
+      String key = nameNode.getTextContent();
+      if (TEST_KEY.equals(key)) {
+        foundSetting = true;
+        Element propertyElem = (Element) nameNode.getParentNode();
+        String val = propertyElem.getElementsByTagName("value").
+            item(0).getTextContent();
+        assertEquals(TEST_VAL, val);
+      }
+    }
+    assertTrue(foundSetting);
+  }
+
+  @Test
+  public void testBadFormat() throws Exception {
+    StringWriter sw = new StringWriter();
+    try {
+      HddsConfServlet.writeResponse(getTestConf(), sw, "not a format", null);
+      fail("writeResponse with bad format didn't throw!");
+    } catch (HddsConfServlet.BadFormatException bfe) {
+      // expected
+    }
+    assertEquals("", sw.toString());
+  }
+
+  private String getResultWithCmd(OzoneConfiguration conf, String cmd)
+      throws Exception {
+    StringWriter sw = null;
+    PrintWriter pw = null;
+    HddsConfServlet service = null;
+    try {
+      service = new HddsConfServlet();
+      ServletConfig servletConf = mock(ServletConfig.class);
+      ServletContext context = mock(ServletContext.class);
+      service.init(servletConf);
+      when(context.getAttribute(HttpServer2.CONF_CONTEXT_ATTRIBUTE))
+          .thenReturn(conf);
+      when(service.getServletContext()).thenReturn(context);
+      HttpServletRequest request = mock(HttpServletRequest.class);
+      when(request.getHeader(HttpHeaders.ACCEPT)).
+          thenReturn(TEST_FORMATS.get(null));
+      when(request.getParameter("cmd")).thenReturn(cmd);
+      
when(request.getParameter("tags")).thenReturn(ConfigTag.DEBUG.toString());
+      HttpServletResponse response = mock(HttpServletResponse.class);
+      sw = new StringWriter();
+      pw = new PrintWriter(sw);
+      when(response.getWriter()).thenReturn(pw);
+      // response request
+      service.doGet(request, response);
+      if (cmd.equals("illegal")) {
+        Mockito.verify(response)
+            .sendError(
+                Mockito.eq(HttpServletResponse.SC_NOT_FOUND),
+                Mockito.eq("illegal is not a valid command."));
+      }
+      String result = sw.toString().trim();
+      return result;
+    } finally {
+      if (sw != null) {
+        sw.close();
+      }
+      if (pw != null) {
+        pw.close();
+      }
+      if (service != null) {
+        service.destroy();
+      }
+    }
+  }
+
+  private void verifyGetProperty(OzoneConfiguration conf, String format,
+      String propertyName) throws Exception {
+    StringWriter sw = null;
+    PrintWriter pw = null;
+    HddsConfServlet service = null;
+    try {
+      service = new HddsConfServlet();
+      ServletConfig servletConf = mock(ServletConfig.class);
+      ServletContext context = mock(ServletContext.class);
+      service.init(servletConf);
+      when(context.getAttribute(HttpServer2.CONF_CONTEXT_ATTRIBUTE))
+          .thenReturn(conf);
+      when(service.getServletContext()).thenReturn(context);
+
+      HttpServletRequest request = mock(HttpServletRequest.class);
+      when(request.getHeader(HttpHeaders.ACCEPT)).
+          thenReturn(TEST_FORMATS.get(format));
+      when(request.getParameter("name")).thenReturn(propertyName);
+
+      HttpServletResponse response = mock(HttpServletResponse.class);
+      sw = new StringWriter();
+      pw = new PrintWriter(sw);
+      when(response.getWriter()).thenReturn(pw);
+
+      // response request
+      service.doGet(request, response);
+      String result = sw.toString().trim();
+
+      // if property name is null or empty, expect all properties
+      // in the response
+      if (Strings.isNullOrEmpty(propertyName)) {
+        for (Map.Entry<String, String> entry : TEST_PROPERTIES.entrySet()) {
+          assertTrue(result.contains(entry.getKey()) &&
+                  result.contains(entry.getValue()));
+        }
+      } else {
+        if (conf.get(propertyName) != null) {
+          // if property name is not empty and property is found
+          assertTrue(result.contains(propertyName));
+          for (Map.Entry<String, String> entry : TEST_PROPERTIES.entrySet()) {
+            if (!entry.getKey().equals(propertyName)) {
+              assertFalse(result.contains(entry.getKey()));
+            }
+          }
+        } else {
+          // if property name is not empty, and it's not in configuration
+          // expect proper error code and error message is set to the response
+          Mockito.verify(response)
+              .sendError(
+                  Mockito.eq(HttpServletResponse.SC_NOT_FOUND),
+                  Mockito.eq("Property " + propertyName + " not found"));
+        }
+      }
+    } finally {
+      if (sw != null) {
+        sw.close();
+      }
+      if (pw != null) {
+        pw.close();
+      }
+      if (service != null) {
+        service.destroy();
+      }
+    }
+  }
+
+  private OzoneConfiguration getTestConf() {
+    OzoneConfiguration testConf = new OzoneConfiguration();
+    testConf.set(TEST_KEY, TEST_VAL);
+    return testConf;
+  }
+
+  private OzoneConfiguration getPropertiesConf() {
+    OzoneConfiguration testConf = new OzoneConfiguration();
+    for (Map.Entry<String, String> entry : TEST_PROPERTIES.entrySet()) {
+      testConf.set(entry.getKey(), entry.getValue());
+    }
+    return testConf;
+  }
+
+  /**
+  * Configuration value for test.
+  */
+  @ConfigGroup(prefix = "ozone.test")
+  public static class OzoneTestConfig {
+    @Config(
+        key = "test.key",
+        defaultValue = "value1",
+        type = ConfigType.STRING,
+        description = "Test get config by tag",
+        tags = ConfigTag.DEBUG)
+    private String testTag = "value1";
+  }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to