You are viewing a plain text version of this content. The canonical link for it is here.
Posted to mapreduce-commits@hadoop.apache.org by ac...@apache.org on 2011/12/29 09:06:05 UTC

svn commit: r1225463 [3/3] - in /hadoop/common/trunk/hadoop-mapreduce-project: ./ hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/webapp/ hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/...

Added: hadoop/common/trunk/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesApps.java
URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesApps.java?rev=1225463&view=auto
==============================================================================
--- hadoop/common/trunk/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesApps.java (added)
+++ hadoop/common/trunk/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesApps.java Thu Dec 29 08:06:04 2011
@@ -0,0 +1,756 @@
+/**
+ * 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.hadoop.yarn.server.resourcemanager.webapp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.StringReader;
+
+import javax.ws.rs.core.MediaType;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.yarn.server.resourcemanager.MockAM;
+import org.apache.hadoop.yarn.server.resourcemanager.MockNM;
+import org.apache.hadoop.yarn.server.resourcemanager.MockRM;
+import org.apache.hadoop.yarn.server.resourcemanager.RMContext;
+import org.apache.hadoop.yarn.server.resourcemanager.ResourceManager;
+import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMApp;
+import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMAppState;
+import org.apache.hadoop.yarn.server.security.ApplicationACLsManager;
+import org.apache.hadoop.yarn.webapp.GenericExceptionHandler;
+import org.apache.hadoop.yarn.webapp.WebServicesTestUtils;
+import org.codehaus.jettison.json.JSONArray;
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
+import org.junit.Before;
+import org.junit.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.servlet.GuiceServletContextListener;
+import com.google.inject.servlet.ServletModule;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.ClientResponse.Status;
+import com.sun.jersey.api.client.UniformInterfaceException;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
+import com.sun.jersey.test.framework.JerseyTest;
+import com.sun.jersey.test.framework.WebAppDescriptor;
+
+public class TestRMWebServicesApps extends JerseyTest {
+
+  private static MockRM rm;
+
+  private Injector injector = Guice.createInjector(new ServletModule() {
+    @Override
+    protected void configureServlets() {
+      bind(JAXBContextResolver.class);
+      bind(RMWebServices.class);
+      bind(GenericExceptionHandler.class);
+      rm = new MockRM(new Configuration());
+      bind(ResourceManager.class).toInstance(rm);
+      bind(RMContext.class).toInstance(rm.getRMContext());
+      bind(ApplicationACLsManager.class).toInstance(
+          rm.getApplicationACLsManager());
+      serve("/*").with(GuiceContainer.class);
+    }
+  });
+
+  public class GuiceServletConfig extends GuiceServletContextListener {
+
+    @Override
+    protected Injector getInjector() {
+      return injector;
+    }
+  }
+
+  @Before
+  @Override
+  public void setUp() throws Exception {
+    super.setUp();
+  }
+
+  public TestRMWebServicesApps() {
+    super(new WebAppDescriptor.Builder(
+        "org.apache.hadoop.yarn.server.resourcemanager.webapp")
+        .contextListenerClass(GuiceServletConfig.class)
+        .filterClass(com.google.inject.servlet.GuiceFilter.class)
+        .contextPath("jersey-guice-filter").servletPath("/").build());
+  }
+
+  @Test
+  public void testApps() throws JSONException, Exception {
+    rm.start();
+    MockNM amNodeManager = rm.registerNode("amNM:1234", 2048);
+    RMApp app1 = rm.submitApp(1024);
+    amNodeManager.nodeHeartbeat(true);
+    testAppsHelper("apps", app1, MediaType.APPLICATION_JSON);
+    rm.stop();
+  }
+
+  @Test
+  public void testAppsSlash() throws JSONException, Exception {
+    rm.start();
+    MockNM amNodeManager = rm.registerNode("amNM:1234", 2048);
+    RMApp app1 = rm.submitApp(1024);
+    amNodeManager.nodeHeartbeat(true);
+    testAppsHelper("apps/", app1, MediaType.APPLICATION_JSON);
+    rm.stop();
+  }
+
+  @Test
+  public void testAppsDefault() throws JSONException, Exception {
+    rm.start();
+    MockNM amNodeManager = rm.registerNode("amNM:1234", 2048);
+    RMApp app1 = rm.submitApp(1024);
+    amNodeManager.nodeHeartbeat(true);
+    testAppsHelper("apps/", app1, "");
+    rm.stop();
+  }
+
+  @Test
+  public void testAppsXML() throws JSONException, Exception {
+    rm.start();
+    MockNM amNodeManager = rm.registerNode("amNM:1234", 2048);
+    RMApp app1 = rm.submitApp(1024, "testwordcount", "user1");
+    amNodeManager.nodeHeartbeat(true);
+    WebResource r = resource();
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("apps").accept(MediaType.APPLICATION_XML)
+        .get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_XML_TYPE, response.getType());
+    String xml = response.getEntity(String.class);
+    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+    DocumentBuilder db = dbf.newDocumentBuilder();
+    InputSource is = new InputSource();
+    is.setCharacterStream(new StringReader(xml));
+    Document dom = db.parse(is);
+    NodeList nodesApps = dom.getElementsByTagName("apps");
+    assertEquals("incorrect number of elements", 1, nodesApps.getLength());
+    NodeList nodes = dom.getElementsByTagName("app");
+    assertEquals("incorrect number of elements", 1, nodes.getLength());
+    verifyAppsXML(nodes, app1);
+    rm.stop();
+  }
+
+  @Test
+  public void testAppsXMLMulti() throws JSONException, Exception {
+    rm.start();
+    MockNM amNodeManager = rm.registerNode("amNM:1234", 2048);
+    rm.submitApp(1024, "testwordcount", "user1");
+    rm.submitApp(2048, "testwordcount2", "user1");
+
+    amNodeManager.nodeHeartbeat(true);
+    WebResource r = resource();
+
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("apps").accept(MediaType.APPLICATION_XML)
+        .get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_XML_TYPE, response.getType());
+    String xml = response.getEntity(String.class);
+    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+    DocumentBuilder db = dbf.newDocumentBuilder();
+    InputSource is = new InputSource();
+    is.setCharacterStream(new StringReader(xml));
+    Document dom = db.parse(is);
+    NodeList nodesApps = dom.getElementsByTagName("apps");
+    assertEquals("incorrect number of elements", 1, nodesApps.getLength());
+    NodeList nodes = dom.getElementsByTagName("app");
+    assertEquals("incorrect number of elements", 2, nodes.getLength());
+    rm.stop();
+  }
+
+  public void testAppsHelper(String path, RMApp app, String media)
+      throws JSONException, Exception {
+    WebResource r = resource();
+
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path(path).accept(media).get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+    JSONObject json = response.getEntity(JSONObject.class);
+    assertEquals("incorrect number of elements", 1, json.length());
+    JSONObject apps = json.getJSONObject("apps");
+    assertEquals("incorrect number of elements", 1, apps.length());
+    JSONArray array = apps.getJSONArray("app");
+    assertEquals("incorrect number of elements", 1, array.length());
+    verifyAppInfo(array.getJSONObject(0), app);
+
+  }
+
+  @Test
+  public void testAppsQueryState() throws JSONException, Exception {
+    rm.start();
+    MockNM amNodeManager = rm.registerNode("amNM:1234", 2048);
+    RMApp app1 = rm.submitApp(1024);
+    amNodeManager.nodeHeartbeat(true);
+    WebResource r = resource();
+
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("apps").queryParam("state", RMAppState.ACCEPTED.toString())
+        .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+    JSONObject json = response.getEntity(JSONObject.class);
+    assertEquals("incorrect number of elements", 1, json.length());
+    JSONObject apps = json.getJSONObject("apps");
+    assertEquals("incorrect number of elements", 1, apps.length());
+    JSONArray array = apps.getJSONArray("app");
+    assertEquals("incorrect number of elements", 1, array.length());
+    verifyAppInfo(array.getJSONObject(0), app1);
+    rm.stop();
+  }
+
+  @Test
+  public void testAppsQueryStateNone() throws JSONException, Exception {
+    rm.start();
+    MockNM amNodeManager = rm.registerNode("amNM:1234", 2048);
+    rm.submitApp(1024);
+    amNodeManager.nodeHeartbeat(true);
+    WebResource r = resource();
+
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("apps").queryParam("state", RMAppState.RUNNING.toString())
+        .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+    JSONObject json = response.getEntity(JSONObject.class);
+    assertEquals("incorrect number of elements", 1, json.length());
+    assertEquals("apps is not null", JSONObject.NULL, json.get("apps"));
+    rm.stop();
+  }
+
+  @Test
+  public void testAppsQueryStateInvalid() throws JSONException, Exception {
+    rm.start();
+    MockNM amNodeManager = rm.registerNode("amNM:1234", 2048);
+    rm.submitApp(1024);
+    amNodeManager.nodeHeartbeat(true);
+    WebResource r = resource();
+
+    try {
+      r.path("ws").path("v1").path("cluster").path("apps")
+          .queryParam("state", "INVALID_test")
+          .accept(MediaType.APPLICATION_JSON).get(JSONObject.class);
+      fail("should have thrown exception on invalid state query");
+    } catch (UniformInterfaceException ue) {
+      ClientResponse response = ue.getResponse();
+      assertEquals(Status.BAD_REQUEST, response.getClientResponseStatus());
+      assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+      JSONObject msg = response.getEntity(JSONObject.class);
+      JSONObject exception = msg.getJSONObject("RemoteException");
+      assertEquals("incorrect number of elements", 3, exception.length());
+      String message = exception.getString("message");
+      String type = exception.getString("exception");
+      String classname = exception.getString("javaClassName");
+      WebServicesTestUtils
+          .checkStringMatch(
+              "exception message",
+              "No enum const class org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMAppState.INVALID_test",
+              message);
+      WebServicesTestUtils.checkStringMatch("exception type",
+          "IllegalArgumentException", type);
+      WebServicesTestUtils.checkStringMatch("exception classname",
+          "java.lang.IllegalArgumentException", classname);
+
+    } finally {
+      rm.stop();
+    }
+  }
+
+  @Test
+  public void testAppsQueryUser() throws JSONException, Exception {
+    rm.start();
+    MockNM amNodeManager = rm.registerNode("amNM:1234", 2048);
+    rm.submitApp(1024);
+    rm.submitApp(1024);
+
+    amNodeManager.nodeHeartbeat(true);
+    WebResource r = resource();
+    ClientResponse response = r
+        .path("ws")
+        .path("v1")
+        .path("cluster")
+        .path("apps")
+        .queryParam("user",
+            UserGroupInformation.getCurrentUser().getShortUserName())
+        .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+    JSONObject json = response.getEntity(JSONObject.class);
+
+    assertEquals("incorrect number of elements", 1, json.length());
+    JSONObject apps = json.getJSONObject("apps");
+    assertEquals("incorrect number of elements", 1, apps.length());
+    JSONArray array = apps.getJSONArray("app");
+    assertEquals("incorrect number of elements", 2, array.length());
+    rm.stop();
+  }
+
+  @Test
+  public void testAppsQueryQueue() throws JSONException, Exception {
+    rm.start();
+    MockNM amNodeManager = rm.registerNode("amNM:1234", 2048);
+    rm.submitApp(1024);
+    rm.submitApp(1024);
+
+    amNodeManager.nodeHeartbeat(true);
+    WebResource r = resource();
+
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("apps").queryParam("queue", "default")
+        .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+    JSONObject json = response.getEntity(JSONObject.class);
+    assertEquals("incorrect number of elements", 1, json.length());
+    JSONObject apps = json.getJSONObject("apps");
+    assertEquals("incorrect number of elements", 1, apps.length());
+    JSONArray array = apps.getJSONArray("app");
+    assertEquals("incorrect number of elements", 2, array.length());
+    rm.stop();
+  }
+
+  @Test
+  public void testAppsQueryLimit() throws JSONException, Exception {
+    rm.start();
+    rm.registerNode("amNM:1234", 2048);
+    rm.submitApp(1024);
+    rm.submitApp(1024);
+    rm.submitApp(1024);
+    WebResource r = resource();
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("apps").queryParam("limit", "2")
+        .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+    JSONObject json = response.getEntity(JSONObject.class);
+    assertEquals("incorrect number of elements", 1, json.length());
+    JSONObject apps = json.getJSONObject("apps");
+    assertEquals("incorrect number of elements", 1, apps.length());
+    JSONArray array = apps.getJSONArray("app");
+    assertEquals("incorrect number of elements", 2, array.length());
+    rm.stop();
+  }
+
+  @Test
+  public void testAppsQueryStartBegin() throws JSONException, Exception {
+    rm.start();
+    long start = System.currentTimeMillis();
+    Thread.sleep(1);
+    rm.registerNode("amNM:1234", 2048);
+    rm.submitApp(1024);
+    rm.submitApp(1024);
+    rm.submitApp(1024);
+    WebResource r = resource();
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("apps").queryParam("startedTimeBegin", String.valueOf(start))
+        .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+    JSONObject json = response.getEntity(JSONObject.class);
+    assertEquals("incorrect number of elements", 1, json.length());
+    JSONObject apps = json.getJSONObject("apps");
+    assertEquals("incorrect number of elements", 1, apps.length());
+    JSONArray array = apps.getJSONArray("app");
+    assertEquals("incorrect number of elements", 3, array.length());
+    rm.stop();
+  }
+
+  @Test
+  public void testAppsQueryStartBeginSome() throws JSONException, Exception {
+    rm.start();
+    rm.registerNode("amNM:1234", 2048);
+    rm.submitApp(1024);
+    rm.submitApp(1024);
+    long start = System.currentTimeMillis();
+    Thread.sleep(1);
+    rm.submitApp(1024);
+    WebResource r = resource();
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("apps").queryParam("startedTimeBegin", String.valueOf(start))
+        .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+    JSONObject json = response.getEntity(JSONObject.class);
+    assertEquals("incorrect number of elements", 1, json.length());
+    JSONObject apps = json.getJSONObject("apps");
+    assertEquals("incorrect number of elements", 1, apps.length());
+    JSONArray array = apps.getJSONArray("app");
+    assertEquals("incorrect number of elements", 1, array.length());
+    rm.stop();
+  }
+
+  @Test
+  public void testAppsQueryStartEnd() throws JSONException, Exception {
+    rm.start();
+    rm.registerNode("amNM:1234", 2048);
+    long end = System.currentTimeMillis();
+    Thread.sleep(1);
+    rm.submitApp(1024);
+    rm.submitApp(1024);
+    rm.submitApp(1024);
+    WebResource r = resource();
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("apps").queryParam("startedTimeEnd", String.valueOf(end))
+        .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+    JSONObject json = response.getEntity(JSONObject.class);
+    assertEquals("incorrect number of elements", 1, json.length());
+    assertEquals("apps is not null", JSONObject.NULL, json.get("apps"));
+    rm.stop();
+  }
+
+  @Test
+  public void testAppsQueryStartBeginEnd() throws JSONException, Exception {
+    rm.start();
+    rm.registerNode("amNM:1234", 2048);
+    long start = System.currentTimeMillis();
+    Thread.sleep(1);
+    rm.submitApp(1024);
+    rm.submitApp(1024);
+    long end = System.currentTimeMillis();
+    Thread.sleep(1);
+    rm.submitApp(1024);
+    WebResource r = resource();
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("apps").queryParam("startedTimeBegin", String.valueOf(start))
+        .queryParam("startedTimeEnd", String.valueOf(end))
+        .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+    JSONObject json = response.getEntity(JSONObject.class);
+    assertEquals("incorrect number of elements", 1, json.length());
+    JSONObject apps = json.getJSONObject("apps");
+    assertEquals("incorrect number of elements", 1, apps.length());
+    JSONArray array = apps.getJSONArray("app");
+    assertEquals("incorrect number of elements", 2, array.length());
+    rm.stop();
+  }
+
+  @Test
+  public void testAppsQueryFinishBegin() throws JSONException, Exception {
+    rm.start();
+    MockNM amNodeManager = rm.registerNode("amNM:1234", 2048);
+    long start = System.currentTimeMillis();
+    Thread.sleep(1);
+    RMApp app1 = rm.submitApp(1024);
+    amNodeManager.nodeHeartbeat(true);
+    // finish App
+    MockAM am = rm
+        .sendAMLaunched(app1.getCurrentAppAttempt().getAppAttemptId());
+    am.registerAppAttempt();
+    am.unregisterAppAttempt();
+    rm.submitApp(1024);
+    rm.submitApp(1024);
+
+    WebResource r = resource();
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("apps").queryParam("finishedTimeBegin", String.valueOf(start))
+        .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+    JSONObject json = response.getEntity(JSONObject.class);
+    assertEquals("incorrect number of elements", 1, json.length());
+    JSONObject apps = json.getJSONObject("apps");
+    assertEquals("incorrect number of elements", 1, apps.length());
+    JSONArray array = apps.getJSONArray("app");
+    assertEquals("incorrect number of elements", 1, array.length());
+    rm.stop();
+  }
+
+  @Test
+  public void testAppsQueryFinishEnd() throws JSONException, Exception {
+    rm.start();
+    MockNM amNodeManager = rm.registerNode("amNM:1234", 2048);
+    RMApp app1 = rm.submitApp(1024);
+    amNodeManager.nodeHeartbeat(true);
+    // finish App
+    MockAM am = rm
+        .sendAMLaunched(app1.getCurrentAppAttempt().getAppAttemptId());
+    am.registerAppAttempt();
+    am.unregisterAppAttempt();
+
+    rm.submitApp(1024);
+    rm.submitApp(1024);
+    long end = System.currentTimeMillis();
+
+    WebResource r = resource();
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("apps").queryParam("finishedTimeEnd", String.valueOf(end))
+        .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+    JSONObject json = response.getEntity(JSONObject.class);
+    assertEquals("incorrect number of elements", 1, json.length());
+    JSONObject apps = json.getJSONObject("apps");
+    assertEquals("incorrect number of elements", 1, apps.length());
+    JSONArray array = apps.getJSONArray("app");
+    assertEquals("incorrect number of elements", 3, array.length());
+    rm.stop();
+  }
+
+  @Test
+  public void testAppsQueryFinishBeginEnd() throws JSONException, Exception {
+    rm.start();
+    MockNM amNodeManager = rm.registerNode("amNM:1234", 2048);
+    long start = System.currentTimeMillis();
+    Thread.sleep(1);
+    RMApp app1 = rm.submitApp(1024);
+    amNodeManager.nodeHeartbeat(true);
+    // finish App
+    MockAM am = rm
+        .sendAMLaunched(app1.getCurrentAppAttempt().getAppAttemptId());
+    am.registerAppAttempt();
+    am.unregisterAppAttempt();
+
+    rm.submitApp(1024);
+    rm.submitApp(1024);
+    long end = System.currentTimeMillis();
+
+    WebResource r = resource();
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("apps").queryParam("finishedTimeBegin", String.valueOf(start))
+        .queryParam("finishedTimeEnd", String.valueOf(end))
+        .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+    JSONObject json = response.getEntity(JSONObject.class);
+    assertEquals("incorrect number of elements", 1, json.length());
+    JSONObject apps = json.getJSONObject("apps");
+    assertEquals("incorrect number of elements", 1, apps.length());
+    JSONArray array = apps.getJSONArray("app");
+    assertEquals("incorrect number of elements", 1, array.length());
+    rm.stop();
+  }
+
+  @Test
+  public void testSingleApp() throws JSONException, Exception {
+    rm.start();
+    MockNM amNodeManager = rm.registerNode("amNM:1234", 2048);
+    RMApp app1 = rm.submitApp(1024, "testwordcount", "user1");
+    amNodeManager.nodeHeartbeat(true);
+    testSingleAppsHelper(app1.getApplicationId().toString(), app1,
+        MediaType.APPLICATION_JSON);
+    rm.stop();
+  }
+
+  @Test
+  public void testSingleAppsSlash() throws JSONException, Exception {
+    rm.start();
+    MockNM amNodeManager = rm.registerNode("amNM:1234", 2048);
+    RMApp app1 = rm.submitApp(1024);
+    amNodeManager.nodeHeartbeat(true);
+    testSingleAppsHelper(app1.getApplicationId().toString() + "/", app1,
+        MediaType.APPLICATION_JSON);
+    rm.stop();
+  }
+
+  @Test
+  public void testSingleAppsDefault() throws JSONException, Exception {
+    rm.start();
+    MockNM amNodeManager = rm.registerNode("amNM:1234", 2048);
+    RMApp app1 = rm.submitApp(1024);
+    amNodeManager.nodeHeartbeat(true);
+    testSingleAppsHelper(app1.getApplicationId().toString() + "/", app1, "");
+    rm.stop();
+  }
+
+  @Test
+  public void testInvalidApp() throws JSONException, Exception {
+    rm.start();
+    MockNM amNodeManager = rm.registerNode("amNM:1234", 2048);
+    rm.submitApp(1024);
+    amNodeManager.nodeHeartbeat(true);
+    WebResource r = resource();
+
+    try {
+      r.path("ws").path("v1").path("cluster").path("apps")
+          .path("application_invalid_12").accept(MediaType.APPLICATION_JSON)
+          .get(JSONObject.class);
+      fail("should have thrown exception on invalid appid");
+    } catch (UniformInterfaceException ue) {
+      ClientResponse response = ue.getResponse();
+
+      assertEquals(Status.BAD_REQUEST, response.getClientResponseStatus());
+      assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+      JSONObject msg = response.getEntity(JSONObject.class);
+      JSONObject exception = msg.getJSONObject("RemoteException");
+      assertEquals("incorrect number of elements", 3, exception.length());
+      String message = exception.getString("message");
+      String type = exception.getString("exception");
+      String classname = exception.getString("javaClassName");
+      WebServicesTestUtils.checkStringMatch("exception message",
+          "For input string: \"invalid\"", message);
+      WebServicesTestUtils.checkStringMatch("exception type",
+          "NumberFormatException", type);
+      WebServicesTestUtils.checkStringMatch("exception classname",
+          "java.lang.NumberFormatException", classname);
+
+    } finally {
+      rm.stop();
+    }
+  }
+
+  @Test
+  public void testNonexistApp() throws JSONException, Exception {
+    rm.start();
+    MockNM amNodeManager = rm.registerNode("amNM:1234", 2048);
+    rm.submitApp(1024, "testwordcount", "user1");
+    amNodeManager.nodeHeartbeat(true);
+    WebResource r = resource();
+
+    try {
+      r.path("ws").path("v1").path("cluster").path("apps")
+          .path("application_00000_0099").accept(MediaType.APPLICATION_JSON)
+          .get(JSONObject.class);
+      fail("should have thrown exception on invalid appid");
+    } catch (UniformInterfaceException ue) {
+      ClientResponse response = ue.getResponse();
+
+      assertEquals(Status.NOT_FOUND, response.getClientResponseStatus());
+      assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+
+      JSONObject msg = response.getEntity(JSONObject.class);
+      JSONObject exception = msg.getJSONObject("RemoteException");
+      assertEquals("incorrect number of elements", 3, exception.length());
+      String message = exception.getString("message");
+      String type = exception.getString("exception");
+      String classname = exception.getString("javaClassName");
+      WebServicesTestUtils.checkStringMatch("exception message",
+          "java.lang.Exception: app with id: application_00000_0099 not found",
+          message);
+      WebServicesTestUtils.checkStringMatch("exception type",
+          "NotFoundException", type);
+      WebServicesTestUtils.checkStringMatch("exception classname",
+          "org.apache.hadoop.yarn.webapp.NotFoundException", classname);
+    } finally {
+      rm.stop();
+    }
+  }
+
+  public void testSingleAppsHelper(String path, RMApp app, String media)
+      throws JSONException, Exception {
+    WebResource r = resource();
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("apps").path(path).accept(media).get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+    JSONObject json = response.getEntity(JSONObject.class);
+
+    assertEquals("incorrect number of elements", 1, json.length());
+    verifyAppInfo(json.getJSONObject("app"), app);
+  }
+
+  @Test
+  public void testSingleAppsXML() throws JSONException, Exception {
+    rm.start();
+    MockNM amNodeManager = rm.registerNode("amNM:1234", 2048);
+    RMApp app1 = rm.submitApp(1024, "testwordcount", "user1");
+    amNodeManager.nodeHeartbeat(true);
+    WebResource r = resource();
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("apps").path(app1.getApplicationId().toString())
+        .accept(MediaType.APPLICATION_XML).get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_XML_TYPE, response.getType());
+    String xml = response.getEntity(String.class);
+
+    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+    DocumentBuilder db = dbf.newDocumentBuilder();
+    InputSource is = new InputSource();
+    is.setCharacterStream(new StringReader(xml));
+    Document dom = db.parse(is);
+    NodeList nodes = dom.getElementsByTagName("app");
+    assertEquals("incorrect number of elements", 1, nodes.getLength());
+    verifyAppsXML(nodes, app1);
+    rm.stop();
+  }
+
+  public void verifyAppsXML(NodeList nodes, RMApp app) throws JSONException,
+      Exception {
+
+     for (int i = 0; i < nodes.getLength(); i++) {
+      Element element = (Element) nodes.item(i);
+
+      verifyAppInfoGeneric(app,
+          WebServicesTestUtils.getXmlString(element, "id"),
+          WebServicesTestUtils.getXmlString(element, "user"),
+          WebServicesTestUtils.getXmlString(element, "name"),
+          WebServicesTestUtils.getXmlString(element, "queue"),
+          WebServicesTestUtils.getXmlString(element, "state"),
+          WebServicesTestUtils.getXmlString(element, "finalStatus"),
+          WebServicesTestUtils.getXmlFloat(element, "progress"),
+          WebServicesTestUtils.getXmlString(element, "trackingUI"),
+          WebServicesTestUtils.getXmlString(element, "diagnostics"),
+          WebServicesTestUtils.getXmlLong(element, "clusterId"),
+          WebServicesTestUtils.getXmlLong(element, "startedTime"),
+          WebServicesTestUtils.getXmlLong(element, "finishedTime"),
+          WebServicesTestUtils.getXmlLong(element, "elapsedTime"),
+          WebServicesTestUtils.getXmlString(element, "amHostHttpAddress"),
+          WebServicesTestUtils.getXmlString(element, "amContainerLogs"));
+    }
+  }
+
+  public void verifyAppInfo(JSONObject info, RMApp app) throws JSONException,
+      Exception {
+
+    // 15 because trackingUrl not assigned yet
+    assertEquals("incorrect number of elements", 15, info.length());
+
+    verifyAppInfoGeneric(app, info.getString("id"), info.getString("user"),
+        info.getString("name"), info.getString("queue"),
+        info.getString("state"), info.getString("finalStatus"),
+        (float) info.getDouble("progress"), info.getString("trackingUI"),
+        info.getString("diagnostics"), info.getLong("clusterId"),
+        info.getLong("startedTime"), info.getLong("finishedTime"),
+        info.getLong("elapsedTime"), info.getString("amHostHttpAddress"),
+        info.getString("amContainerLogs"));
+  }
+
+  public void verifyAppInfoGeneric(RMApp app, String id, String user,
+      String name, String queue, String state, String finalStatus,
+      float progress, String trackingUI, String diagnostics, long clusterId,
+      long startedTime, long finishedTime, long elapsedTime,
+      String amHostHttpAddress, String amContainerLogs) throws JSONException,
+      Exception {
+
+    WebServicesTestUtils.checkStringMatch("id", app.getApplicationId()
+        .toString(), id);
+    WebServicesTestUtils.checkStringMatch("user", app.getUser(), user);
+    WebServicesTestUtils.checkStringMatch("name", app.getName(), name);
+    WebServicesTestUtils.checkStringMatch("queue", app.getQueue(), queue);
+    WebServicesTestUtils.checkStringMatch("state", app.getState().toString(),
+        state);
+    WebServicesTestUtils.checkStringMatch("finalStatus", app
+        .getFinalApplicationStatus().toString(), finalStatus);
+    assertEquals("progress doesn't match", 0, progress, 0.0);
+    WebServicesTestUtils.checkStringMatch("trackingUI", "UNASSIGNED",
+        trackingUI);
+    WebServicesTestUtils.checkStringMatch("diagnostics", app.getDiagnostics()
+        .toString(), diagnostics);
+    assertEquals("clusterId doesn't match", ResourceManager.clusterTimeStamp,
+        clusterId);
+    assertEquals("startedTime doesn't match", app.getStartTime(), startedTime);
+    assertEquals("finishedTime doesn't match", app.getFinishTime(),
+        finishedTime);
+    assertTrue("elapsed time not greater than 0", elapsedTime > 0);
+    WebServicesTestUtils.checkStringMatch("amHostHttpAddress", app
+        .getCurrentAppAttempt().getMasterContainer().getNodeHttpAddress(),
+        amHostHttpAddress);
+    assertTrue("amContainerLogs doesn't match",
+        amContainerLogs.startsWith("http://"));
+  }
+
+}

Added: hadoop/common/trunk/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesCapacitySched.java
URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesCapacitySched.java?rev=1225463&view=auto
==============================================================================
--- hadoop/common/trunk/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesCapacitySched.java (added)
+++ hadoop/common/trunk/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesCapacitySched.java Thu Dec 29 08:06:04 2011
@@ -0,0 +1,316 @@
+/**
+ * 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.hadoop.yarn.server.resourcemanager.webapp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.StringReader;
+
+import javax.ws.rs.core.MediaType;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.apache.hadoop.yarn.conf.YarnConfiguration;
+import org.apache.hadoop.yarn.server.resourcemanager.MockRM;
+import org.apache.hadoop.yarn.server.resourcemanager.RMContext;
+import org.apache.hadoop.yarn.server.resourcemanager.ResourceManager;
+import org.apache.hadoop.yarn.server.resourcemanager.scheduler.ResourceScheduler;
+import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler;
+import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacitySchedulerConfiguration;
+import org.apache.hadoop.yarn.server.security.ApplicationACLsManager;
+import org.apache.hadoop.yarn.webapp.GenericExceptionHandler;
+import org.apache.hadoop.yarn.webapp.WebServicesTestUtils;
+import org.codehaus.jettison.json.JSONArray;
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
+import org.junit.Before;
+import org.junit.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.servlet.GuiceServletContextListener;
+import com.google.inject.servlet.ServletModule;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
+import com.sun.jersey.test.framework.JerseyTest;
+import com.sun.jersey.test.framework.WebAppDescriptor;
+
+public class TestRMWebServicesCapacitySched extends JerseyTest {
+
+  private static MockRM rm;
+  private CapacitySchedulerConfiguration csConf;
+
+  private Injector injector = Guice.createInjector(new ServletModule() {
+    @Override
+    protected void configureServlets() {
+      bind(JAXBContextResolver.class);
+      bind(RMWebServices.class);
+      bind(GenericExceptionHandler.class);
+      csConf = new CapacitySchedulerConfiguration();
+      csConf.setClass(YarnConfiguration.RM_SCHEDULER, CapacityScheduler.class,
+          ResourceScheduler.class);
+      setupQueueConfiguration(csConf);
+      rm = new MockRM(csConf);
+      bind(ResourceManager.class).toInstance(rm);
+      bind(RMContext.class).toInstance(rm.getRMContext());
+      bind(ApplicationACLsManager.class).toInstance(
+          rm.getApplicationACLsManager());
+      serve("/*").with(GuiceContainer.class);
+    }
+  });
+
+  public class GuiceServletConfig extends GuiceServletContextListener {
+
+    @Override
+    protected Injector getInjector() {
+      return injector;
+    }
+  }
+
+  private static void setupQueueConfiguration(
+      CapacitySchedulerConfiguration conf) {
+
+    // Define top-level queues
+    conf.setQueues(CapacityScheduler.ROOT, new String[] { "a", "b" });
+    conf.setCapacity(CapacityScheduler.ROOT, 100);
+
+    final String A = CapacityScheduler.ROOT + ".a";
+    conf.setCapacity(A, 10);
+    conf.setMaximumCapacity(A, 50);
+
+    final String B = CapacityScheduler.ROOT + ".b";
+    conf.setCapacity(B, 90);
+
+    // Define 2nd-level queues
+    final String A1 = A + ".a1";
+    final String A2 = A + ".a2";
+    conf.setQueues(A, new String[] { "a1", "a2" });
+    conf.setCapacity(A1, 30);
+    conf.setMaximumCapacity(A1, 50);
+
+    conf.setUserLimitFactor(A1, 100.0f);
+    conf.setCapacity(A2, 70);
+    conf.setUserLimitFactor(A2, 100.0f);
+
+    final String B1 = B + ".b1";
+    final String B2 = B + ".b2";
+    final String B3 = B + ".b3";
+    conf.setQueues(B, new String[] { "b1", "b2", "b3" });
+    conf.setCapacity(B1, 50);
+    conf.setUserLimitFactor(B1, 100.0f);
+    conf.setCapacity(B2, 30);
+    conf.setUserLimitFactor(B2, 100.0f);
+    conf.setCapacity(B3, 20);
+    conf.setUserLimitFactor(B3, 100.0f);
+
+  }
+
+  @Before
+  @Override
+  public void setUp() throws Exception {
+    super.setUp();
+  }
+
+  public TestRMWebServicesCapacitySched() {
+    super(new WebAppDescriptor.Builder(
+        "org.apache.hadoop.yarn.server.resourcemanager.webapp")
+        .contextListenerClass(GuiceServletConfig.class)
+        .filterClass(com.google.inject.servlet.GuiceFilter.class)
+        .contextPath("jersey-guice-filter").servletPath("/").build());
+  }
+
+  @Test
+  public void testClusterScheduler() throws JSONException, Exception {
+    WebResource r = resource();
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("scheduler").accept(MediaType.APPLICATION_JSON)
+        .get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+    JSONObject json = response.getEntity(JSONObject.class);
+    verifyClusterScheduler(json);
+  }
+
+  @Test
+  public void testClusterSchedulerSlash() throws JSONException, Exception {
+    WebResource r = resource();
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("scheduler/").accept(MediaType.APPLICATION_JSON)
+        .get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+    JSONObject json = response.getEntity(JSONObject.class);
+    verifyClusterScheduler(json);
+  }
+
+  @Test
+  public void testClusterSchedulerDefault() throws JSONException, Exception {
+    WebResource r = resource();
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("scheduler").get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+    JSONObject json = response.getEntity(JSONObject.class);
+    verifyClusterScheduler(json);
+  }
+
+  @Test
+  public void testClusterSchedulerXML() throws JSONException, Exception {
+    WebResource r = resource();
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("scheduler/").accept(MediaType.APPLICATION_XML)
+        .get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_XML_TYPE, response.getType());
+    String xml = response.getEntity(String.class);
+    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+    DocumentBuilder db = dbf.newDocumentBuilder();
+    InputSource is = new InputSource();
+    is.setCharacterStream(new StringReader(xml));
+    Document dom = db.parse(is);
+    NodeList scheduler = dom.getElementsByTagName("scheduler");
+    assertEquals("incorrect number of elements", 1, scheduler.getLength());
+    NodeList schedulerInfo = dom.getElementsByTagName("schedulerInfo");
+    assertEquals("incorrect number of elements", 1, schedulerInfo.getLength());
+    verifyClusterSchedulerXML(schedulerInfo);
+  }
+
+  public void verifyClusterSchedulerXML(NodeList nodes) throws Exception {
+
+    for (int i = 0; i < nodes.getLength(); i++) {
+      Element element = (Element) nodes.item(i);
+
+      verifyClusterSchedulerGeneric(
+          WebServicesTestUtils.getXmlAttrString(element, "xsi:type"),
+          WebServicesTestUtils.getXmlFloat(element, "usedCapacity"),
+          WebServicesTestUtils.getXmlFloat(element, "capacity"),
+          WebServicesTestUtils.getXmlFloat(element, "maxCapacity"),
+          WebServicesTestUtils.getXmlString(element, "queueName"));
+
+      NodeList queues = element.getElementsByTagName("queues");
+      for (int j = 0; j < queues.getLength(); j++) {
+        Element qElem = (Element) queues.item(j);
+        String qName = WebServicesTestUtils.getXmlString(qElem, "queueName");
+        String q = CapacityScheduler.ROOT + "." + qName;
+        verifySubQueueXML(qElem, q);
+      }
+    }
+  }
+
+  public void verifySubQueueXML(Element qElem, String q) throws Exception {
+
+    verifySubQueueGeneric(q,
+        WebServicesTestUtils.getXmlFloat(qElem, "usedCapacity"),
+        WebServicesTestUtils.getXmlFloat(qElem, "capacity"),
+        WebServicesTestUtils.getXmlFloat(qElem, "maxCapacity"),
+        WebServicesTestUtils.getXmlString(qElem, "queueName"),
+        WebServicesTestUtils.getXmlString(qElem, "state"));
+
+    NodeList queues = qElem.getElementsByTagName("subQueues");
+    if (queues != null) {
+      for (int j = 0; j < queues.getLength(); j++) {
+        Element subqElem = (Element) queues.item(j);
+        String qName = WebServicesTestUtils.getXmlString(subqElem, "queueName");
+        String q2 = q + "." + qName;
+        verifySubQueueXML(subqElem, q2);
+      }
+    }
+  }
+
+  private void verifyClusterScheduler(JSONObject json) throws JSONException,
+      Exception {
+    assertEquals("incorrect number of elements", 1, json.length());
+    JSONObject info = json.getJSONObject("scheduler");
+    assertEquals("incorrect number of elements", 1, info.length());
+    info = info.getJSONObject("schedulerInfo");
+    assertEquals("incorrect number of elements", 6, info.length());
+    verifyClusterSchedulerGeneric(info.getString("type"),
+        (float) info.getDouble("usedCapacity"),
+        (float) info.getDouble("capacity"),
+        (float) info.getDouble("maxCapacity"), info.getString("queueName"));
+
+    JSONArray arr = info.getJSONArray("queues");
+    assertEquals("incorrect number of elements", 2, arr.length());
+
+    // test subqueues
+    for (int i = 0; i < arr.length(); i++) {
+      JSONObject obj = arr.getJSONObject(i);
+      String q = CapacityScheduler.ROOT + "." + obj.getString("queueName");
+      verifySubQueue(obj, q);
+    }
+  }
+
+  private void verifyClusterSchedulerGeneric(String type, float usedCapacity,
+      float capacity, float maxCapacity, String queueName) throws Exception {
+
+    assertTrue("type doesn't match", "capacityScheduler".matches(type));
+    assertEquals("usedCapacity doesn't match", 0, usedCapacity, 1e-3f);
+    assertEquals("capacity doesn't match", 100, capacity, 1e-3f);
+    assertEquals("maxCapacity doesn't match", 100, maxCapacity, 1e-3f);
+    assertTrue("queueName doesn't match", "root".matches(queueName));
+  }
+
+  private void verifySubQueue(JSONObject info, String q) throws JSONException,
+      Exception {
+    if (info.has("subQueues")) {
+      assertEquals("incorrect number of elements", 6, info.length());
+    } else {
+      assertEquals("incorrect number of elements", 5, info.length());
+    }
+    verifySubQueueGeneric(q, (float) info.getDouble("usedCapacity"),
+        (float) info.getDouble("capacity"),
+        (float) info.getDouble("maxCapacity"), info.getString("queueName"),
+        info.getString("state"));
+
+    if (info.has("subQueues")) {
+      JSONArray arr = info.getJSONArray("subQueues");
+      // test subqueues
+      for (int i = 0; i < arr.length(); i++) {
+        JSONObject obj = arr.getJSONObject(i);
+        String q2 = q + "." + obj.getString("queueName");
+        verifySubQueue(obj, q2);
+      }
+    }
+  }
+
+  private void verifySubQueueGeneric(String q, float usedCapacity,
+      float capacity, float maxCapacity, String qname, String state)
+      throws Exception {
+    String[] qArr = q.split("\\.");
+    assertTrue("q name invalid: " + q, qArr.length > 1);
+    String qshortName = qArr[qArr.length - 1];
+
+    assertEquals("usedCapacity doesn't match", 0, usedCapacity, 1e-3f);
+    assertEquals("capacity doesn't match", csConf.getCapacity(q), capacity,
+        1e-3f);
+    float expectCapacity = csConf.getMaximumCapacity(q);
+    if (CapacitySchedulerConfiguration.UNDEFINED == expectCapacity) {
+      expectCapacity = 100;
+    }
+    assertEquals("maxCapacity doesn't match", expectCapacity, maxCapacity,
+        1e-3f);
+    assertTrue("queueName doesn't match, got: " + qname + " expected: " + q,
+        qshortName.matches(qname));
+    assertTrue("state doesn't match",
+        (csConf.getState(q).toString()).matches(state));
+
+  }
+}

Added: hadoop/common/trunk/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesNodes.java
URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesNodes.java?rev=1225463&view=auto
==============================================================================
--- hadoop/common/trunk/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesNodes.java (added)
+++ hadoop/common/trunk/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesNodes.java Thu Dec 29 08:06:04 2011
@@ -0,0 +1,601 @@
+/**
+ * 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.hadoop.yarn.server.resourcemanager.webapp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.io.StringReader;
+import java.util.ArrayList;
+
+import javax.ws.rs.core.MediaType;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.yarn.api.records.ContainerStatus;
+import org.apache.hadoop.yarn.api.records.NodeHealthStatus;
+import org.apache.hadoop.yarn.server.resourcemanager.MockNM;
+import org.apache.hadoop.yarn.server.resourcemanager.MockRM;
+import org.apache.hadoop.yarn.server.resourcemanager.RMContext;
+import org.apache.hadoop.yarn.server.resourcemanager.ResourceManager;
+import org.apache.hadoop.yarn.server.resourcemanager.rmnode.RMNode;
+import org.apache.hadoop.yarn.server.resourcemanager.rmnode.RMNodeImpl;
+import org.apache.hadoop.yarn.server.resourcemanager.rmnode.RMNodeState;
+import org.apache.hadoop.yarn.server.resourcemanager.rmnode.RMNodeStatusEvent;
+import org.apache.hadoop.yarn.server.resourcemanager.scheduler.ResourceScheduler;
+import org.apache.hadoop.yarn.server.resourcemanager.scheduler.SchedulerNodeReport;
+import org.apache.hadoop.yarn.server.security.ApplicationACLsManager;
+import org.apache.hadoop.yarn.webapp.GenericExceptionHandler;
+import org.apache.hadoop.yarn.webapp.WebServicesTestUtils;
+import org.codehaus.jettison.json.JSONArray;
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
+import org.junit.Before;
+import org.junit.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.servlet.GuiceServletContextListener;
+import com.google.inject.servlet.ServletModule;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.ClientResponse.Status;
+import com.sun.jersey.api.client.UniformInterfaceException;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
+import com.sun.jersey.test.framework.JerseyTest;
+import com.sun.jersey.test.framework.WebAppDescriptor;
+
+public class TestRMWebServicesNodes extends JerseyTest {
+
+  private static MockRM rm;
+
+  private Injector injector = Guice.createInjector(new ServletModule() {
+    @Override
+    protected void configureServlets() {
+      bind(JAXBContextResolver.class);
+      bind(RMWebServices.class);
+      bind(GenericExceptionHandler.class);
+      rm = new MockRM(new Configuration());
+      bind(ResourceManager.class).toInstance(rm);
+      bind(RMContext.class).toInstance(rm.getRMContext());
+      bind(ApplicationACLsManager.class).toInstance(
+          rm.getApplicationACLsManager());
+      serve("/*").with(GuiceContainer.class);
+    }
+  });
+
+  public class GuiceServletConfig extends GuiceServletContextListener {
+
+    @Override
+    protected Injector getInjector() {
+      return injector;
+    }
+  }
+
+  @Before
+  @Override
+  public void setUp() throws Exception {
+    super.setUp();
+  }
+
+  public TestRMWebServicesNodes() {
+    super(new WebAppDescriptor.Builder(
+        "org.apache.hadoop.yarn.server.resourcemanager.webapp")
+        .contextListenerClass(GuiceServletConfig.class)
+        .filterClass(com.google.inject.servlet.GuiceFilter.class)
+        .contextPath("jersey-guice-filter").servletPath("/").build());
+  }
+
+  @Test
+  public void testNodes() throws JSONException, Exception {
+    testNodesHelper("nodes", MediaType.APPLICATION_JSON);
+  }
+
+  @Test
+  public void testNodesSlash() throws JSONException, Exception {
+    testNodesHelper("nodes/", MediaType.APPLICATION_JSON);
+  }
+
+  @Test
+  public void testNodesDefault() throws JSONException, Exception {
+    testNodesHelper("nodes/", "");
+  }
+
+  @Test
+  public void testNodesQueryState() throws JSONException, Exception {
+    WebResource r = resource();
+    MockNM nm1 = rm.registerNode("h1:1234", 5120);
+    MockNM nm2 = rm.registerNode("h2:1235", 5121);
+    rm.sendNodeStarted(nm1);
+    rm.NMwaitForState(nm1.getNodeId(), RMNodeState.RUNNING);
+    rm.NMwaitForState(nm2.getNodeId(), RMNodeState.NEW);
+
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("nodes").queryParam("state", RMNodeState.RUNNING.toString())
+        .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
+
+    assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+    JSONObject json = response.getEntity(JSONObject.class);
+    assertEquals("incorrect number of elements", 1, json.length());
+    JSONObject nodes = json.getJSONObject("nodes");
+    assertEquals("incorrect number of elements", 1, nodes.length());
+    JSONArray nodeArray = nodes.getJSONArray("node");
+    assertEquals("incorrect number of elements", 1, nodeArray.length());
+    JSONObject info = nodeArray.getJSONObject(0);
+
+    verifyNodeInfo(info, nm1);
+  }
+
+  @Test
+  public void testNodesQueryStateNone() throws JSONException, Exception {
+    WebResource r = resource();
+    rm.registerNode("h1:1234", 5120);
+    rm.registerNode("h2:1235", 5121);
+
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("nodes")
+        .queryParam("state", RMNodeState.DECOMMISSIONED.toString())
+        .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+    JSONObject json = response.getEntity(JSONObject.class);
+    assertEquals("incorrect number of elements", 1, json.length());
+    assertEquals("nodes is not null", JSONObject.NULL, json.get("nodes"));
+  }
+
+  @Test
+  public void testNodesQueryStateInvalid() throws JSONException, Exception {
+    WebResource r = resource();
+    rm.registerNode("h1:1234", 5120);
+    rm.registerNode("h2:1235", 5121);
+
+    try {
+      r.path("ws").path("v1").path("cluster").path("nodes")
+          .queryParam("state", "BOGUSSTATE").accept(MediaType.APPLICATION_JSON)
+          .get(JSONObject.class);
+
+      fail("should have thrown exception querying invalid state");
+    } catch (UniformInterfaceException ue) {
+      ClientResponse response = ue.getResponse();
+
+      assertEquals(Status.BAD_REQUEST, response.getClientResponseStatus());
+      assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+
+      JSONObject msg = response.getEntity(JSONObject.class);
+      JSONObject exception = msg.getJSONObject("RemoteException");
+      assertEquals("incorrect number of elements", 3, exception.length());
+      String message = exception.getString("message");
+      String type = exception.getString("exception");
+      String classname = exception.getString("javaClassName");
+      WebServicesTestUtils
+          .checkStringMatch(
+              "exception message",
+              "No enum const class org.apache.hadoop.yarn.server.resourcemanager.rmnode.RMNodeState.BOGUSSTATE",
+              message);
+      WebServicesTestUtils.checkStringMatch("exception type",
+          "IllegalArgumentException", type);
+      WebServicesTestUtils.checkStringMatch("exception classname",
+          "java.lang.IllegalArgumentException", classname);
+
+    } finally {
+      rm.stop();
+    }
+  }
+
+  @Test
+  public void testNodesQueryHealthy() throws JSONException, Exception {
+    WebResource r = resource();
+    MockNM nm1 = rm.registerNode("h1:1234", 5120);
+    MockNM nm2 = rm.registerNode("h2:1235", 5121);
+    rm.sendNodeStarted(nm1);
+    rm.NMwaitForState(nm1.getNodeId(), RMNodeState.RUNNING);
+    rm.NMwaitForState(nm2.getNodeId(), RMNodeState.NEW);
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("nodes").queryParam("healthy", "true")
+        .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+    JSONObject json = response.getEntity(JSONObject.class);
+    assertEquals("incorrect number of elements", 1, json.length());
+    JSONObject nodes = json.getJSONObject("nodes");
+    assertEquals("incorrect number of elements", 1, nodes.length());
+    JSONArray nodeArray = nodes.getJSONArray("node");
+    assertEquals("incorrect number of elements", 2, nodeArray.length());
+  }
+
+  @Test
+  public void testNodesQueryHealthyCase() throws JSONException, Exception {
+    WebResource r = resource();
+    MockNM nm1 = rm.registerNode("h1:1234", 5120);
+    MockNM nm2 = rm.registerNode("h2:1235", 5121);
+    rm.sendNodeStarted(nm1);
+    rm.NMwaitForState(nm1.getNodeId(), RMNodeState.RUNNING);
+    rm.NMwaitForState(nm2.getNodeId(), RMNodeState.NEW);
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("nodes").queryParam("healthy", "TRUe")
+        .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+    JSONObject json = response.getEntity(JSONObject.class);
+    assertEquals("incorrect number of elements", 1, json.length());
+    JSONObject nodes = json.getJSONObject("nodes");
+    assertEquals("incorrect number of elements", 1, nodes.length());
+    JSONArray nodeArray = nodes.getJSONArray("node");
+    assertEquals("incorrect number of elements", 2, nodeArray.length());
+
+  }
+
+  @Test
+  public void testNodesQueryHealthyAndState() throws JSONException, Exception {
+    WebResource r = resource();
+    MockNM nm1 = rm.registerNode("h1:1234", 5120);
+    MockNM nm2 = rm.registerNode("h2:1235", 5121);
+    rm.sendNodeStarted(nm1);
+    rm.NMwaitForState(nm2.getNodeId(), RMNodeState.NEW);
+    rm.NMwaitForState(nm1.getNodeId(), RMNodeState.RUNNING);
+    RMNodeImpl node = (RMNodeImpl) rm.getRMContext().getRMNodes()
+        .get(nm1.getNodeId());
+    NodeHealthStatus nodeHealth = node.getNodeHealthStatus();
+    nodeHealth.setHealthReport("test health report");
+    nodeHealth.setIsNodeHealthy(false);
+    node.handle(new RMNodeStatusEvent(nm1.getNodeId(), nodeHealth,
+        new ArrayList<ContainerStatus>(), null, null));
+    rm.NMwaitForState(nm1.getNodeId(), RMNodeState.UNHEALTHY);
+
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("nodes").queryParam("healthy", "true")
+        .queryParam("state", RMNodeState.RUNNING.toString())
+        .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+    JSONObject json = response.getEntity(JSONObject.class);
+    assertEquals("incorrect number of elements", 1, json.length());
+    assertEquals("nodes is not null", JSONObject.NULL, json.get("nodes"));
+  }
+
+  @Test
+  public void testNodesQueryHealthyFalse() throws JSONException, Exception {
+    WebResource r = resource();
+    MockNM nm1 = rm.registerNode("h1:1234", 5120);
+    MockNM nm2 = rm.registerNode("h2:1235", 5121);
+    rm.sendNodeStarted(nm1);
+    rm.NMwaitForState(nm1.getNodeId(), RMNodeState.RUNNING);
+    rm.NMwaitForState(nm2.getNodeId(), RMNodeState.NEW);
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("nodes").queryParam("healthy", "false")
+        .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+    JSONObject json = response.getEntity(JSONObject.class);
+    assertEquals("incorrect number of elements", 1, json.length());
+    assertEquals("nodes is not null", JSONObject.NULL, json.get("nodes"));
+  }
+
+  @Test
+  public void testNodesQueryHealthyInvalid() throws JSONException, Exception {
+    WebResource r = resource();
+    rm.registerNode("h1:1234", 5120);
+    rm.registerNode("h2:1235", 5121);
+
+    try {
+      r.path("ws").path("v1").path("cluster").path("nodes")
+          .queryParam("healthy", "tr").accept(MediaType.APPLICATION_JSON)
+          .get(JSONObject.class);
+      fail("should have thrown exception querying invalid healthy string");
+    } catch (UniformInterfaceException ue) {
+      ClientResponse response = ue.getResponse();
+      assertEquals(Status.BAD_REQUEST, response.getClientResponseStatus());
+      assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+      JSONObject msg = response.getEntity(JSONObject.class);
+      JSONObject exception = msg.getJSONObject("RemoteException");
+      assertEquals("incorrect number of elements", 3, exception.length());
+      String message = exception.getString("message");
+      String type = exception.getString("exception");
+      String classname = exception.getString("javaClassName");
+      WebServicesTestUtils
+          .checkStringMatch(
+              "exception message",
+              "java.lang.Exception: Error: You must specify either true or false to query on health",
+              message);
+      WebServicesTestUtils.checkStringMatch("exception type",
+          "BadRequestException", type);
+      WebServicesTestUtils.checkStringMatch("exception classname",
+          "org.apache.hadoop.yarn.webapp.BadRequestException", classname);
+
+    } finally {
+      rm.stop();
+    }
+  }
+
+  public void testNodesHelper(String path, String media) throws JSONException,
+      Exception {
+    WebResource r = resource();
+    MockNM nm1 = rm.registerNode("h1:1234", 5120);
+    MockNM nm2 = rm.registerNode("h2:1235", 5121);
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path(path).accept(media).get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+    JSONObject json = response.getEntity(JSONObject.class);
+    assertEquals("incorrect number of elements", 1, json.length());
+    JSONObject nodes = json.getJSONObject("nodes");
+    assertEquals("incorrect number of elements", 1, nodes.length());
+    JSONArray nodeArray = nodes.getJSONArray("node");
+    assertEquals("incorrect number of elements", 2, nodeArray.length());
+    JSONObject info = nodeArray.getJSONObject(0);
+    String id = info.get("id").toString();
+
+    if (id.matches("h1:1234")) {
+      verifyNodeInfo(info, nm1);
+      verifyNodeInfo(nodeArray.getJSONObject(1), nm2);
+    } else {
+      verifyNodeInfo(info, nm2);
+      verifyNodeInfo(nodeArray.getJSONObject(1), nm1);
+    }
+  }
+
+  @Test
+  public void testSingleNode() throws JSONException, Exception {
+    rm.registerNode("h1:1234", 5120);
+    MockNM nm2 = rm.registerNode("h2:1235", 5121);
+    testSingleNodeHelper("h2:1235", nm2, MediaType.APPLICATION_JSON);
+  }
+
+  @Test
+  public void testSingleNodeSlash() throws JSONException, Exception {
+    MockNM nm1 = rm.registerNode("h1:1234", 5120);
+    rm.registerNode("h2:1235", 5121);
+    testSingleNodeHelper("h1:1234/", nm1, MediaType.APPLICATION_JSON);
+  }
+
+  @Test
+  public void testSingleNodeDefault() throws JSONException, Exception {
+    MockNM nm1 = rm.registerNode("h1:1234", 5120);
+    rm.registerNode("h2:1235", 5121);
+    testSingleNodeHelper("h1:1234/", nm1, "");
+  }
+
+  public void testSingleNodeHelper(String nodeid, MockNM nm, String media)
+      throws JSONException, Exception {
+    WebResource r = resource();
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("nodes").path(nodeid).accept(media).get(ClientResponse.class);
+
+    assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+    JSONObject json = response.getEntity(JSONObject.class);
+    assertEquals("incorrect number of elements", 1, json.length());
+    JSONObject info = json.getJSONObject("node");
+    verifyNodeInfo(info, nm);
+  }
+
+  @Test
+  public void testNonexistNode() throws JSONException, Exception {
+    rm.registerNode("h1:1234", 5120);
+    rm.registerNode("h2:1235", 5121);
+    WebResource r = resource();
+    try {
+      r.path("ws").path("v1").path("cluster").path("nodes")
+          .path("node_invalid:99").accept(MediaType.APPLICATION_JSON)
+          .get(JSONObject.class);
+
+      fail("should have thrown exception on non-existent nodeid");
+    } catch (UniformInterfaceException ue) {
+      ClientResponse response = ue.getResponse();
+      assertEquals(Status.NOT_FOUND, response.getClientResponseStatus());
+      assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+      JSONObject msg = response.getEntity(JSONObject.class);
+      JSONObject exception = msg.getJSONObject("RemoteException");
+      assertEquals("incorrect number of elements", 3, exception.length());
+      String message = exception.getString("message");
+      String type = exception.getString("exception");
+      String classname = exception.getString("javaClassName");
+      WebServicesTestUtils
+          .checkStringMatch("exception message",
+              "java.lang.Exception: nodeId, node_invalid:99, is not found",
+              message);
+      WebServicesTestUtils.checkStringMatch("exception type",
+          "NotFoundException", type);
+      WebServicesTestUtils.checkStringMatch("exception classname",
+          "org.apache.hadoop.yarn.webapp.NotFoundException", classname);
+
+    } finally {
+      rm.stop();
+    }
+  }
+
+  @Test
+  public void testInvalidNode() throws JSONException, Exception {
+    rm.registerNode("h1:1234", 5120);
+    rm.registerNode("h2:1235", 5121);
+
+    WebResource r = resource();
+    try {
+      r.path("ws").path("v1").path("cluster").path("nodes")
+          .path("node_invalid_foo").accept(MediaType.APPLICATION_JSON)
+          .get(JSONObject.class);
+
+      fail("should have thrown exception on non-existent nodeid");
+    } catch (UniformInterfaceException ue) {
+      ClientResponse response = ue.getResponse();
+
+      assertEquals(Status.BAD_REQUEST, response.getClientResponseStatus());
+      assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+      JSONObject msg = response.getEntity(JSONObject.class);
+      JSONObject exception = msg.getJSONObject("RemoteException");
+      assertEquals("incorrect number of elements", 3, exception.length());
+      String message = exception.getString("message");
+      String type = exception.getString("exception");
+      String classname = exception.getString("javaClassName");
+      WebServicesTestUtils.checkStringMatch("exception message",
+          "Invalid NodeId \\[node_invalid_foo\\]. Expected host:port", message);
+      WebServicesTestUtils.checkStringMatch("exception type",
+          "IllegalArgumentException", type);
+      WebServicesTestUtils.checkStringMatch("exception classname",
+          "java.lang.IllegalArgumentException", classname);
+    } finally {
+      rm.stop();
+    }
+  }
+
+  @Test
+  public void testNodesXML() throws JSONException, Exception {
+    rm.start();
+    WebResource r = resource();
+    MockNM nm1 = rm.registerNode("h1:1234", 5120);
+    // MockNM nm2 = rm.registerNode("h2:1235", 5121);
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("nodes").accept(MediaType.APPLICATION_XML)
+        .get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_XML_TYPE, response.getType());
+    String xml = response.getEntity(String.class);
+    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+    DocumentBuilder db = dbf.newDocumentBuilder();
+    InputSource is = new InputSource();
+    is.setCharacterStream(new StringReader(xml));
+    Document dom = db.parse(is);
+    NodeList nodesApps = dom.getElementsByTagName("nodes");
+    assertEquals("incorrect number of elements", 1, nodesApps.getLength());
+    NodeList nodes = dom.getElementsByTagName("node");
+    assertEquals("incorrect number of elements", 1, nodes.getLength());
+    verifyNodesXML(nodes, nm1);
+    rm.stop();
+  }
+
+  @Test
+  public void testSingleNodesXML() throws JSONException, Exception {
+    rm.start();
+    WebResource r = resource();
+    MockNM nm1 = rm.registerNode("h1:1234", 5120);
+    // MockNM nm2 = rm.registerNode("h2:1235", 5121);
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("nodes").path("h1:1234").accept(MediaType.APPLICATION_XML)
+        .get(ClientResponse.class);
+
+    assertEquals(MediaType.APPLICATION_XML_TYPE, response.getType());
+    String xml = response.getEntity(String.class);
+
+    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+    DocumentBuilder db = dbf.newDocumentBuilder();
+    InputSource is = new InputSource();
+    is.setCharacterStream(new StringReader(xml));
+    Document dom = db.parse(is);
+    NodeList nodes = dom.getElementsByTagName("node");
+    assertEquals("incorrect number of elements", 1, nodes.getLength());
+    verifyNodesXML(nodes, nm1);
+    rm.stop();
+  }
+
+  @Test
+  public void testNodes2XML() throws JSONException, Exception {
+    rm.start();
+    WebResource r = resource();
+    rm.registerNode("h1:1234", 5120);
+    rm.registerNode("h2:1235", 5121);
+    ClientResponse response = r.path("ws").path("v1").path("cluster")
+        .path("nodes").accept(MediaType.APPLICATION_XML)
+        .get(ClientResponse.class);
+    assertEquals(MediaType.APPLICATION_XML_TYPE, response.getType());
+    String xml = response.getEntity(String.class);
+
+    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+    DocumentBuilder db = dbf.newDocumentBuilder();
+    InputSource is = new InputSource();
+    is.setCharacterStream(new StringReader(xml));
+    Document dom = db.parse(is);
+    NodeList nodesApps = dom.getElementsByTagName("nodes");
+    assertEquals("incorrect number of elements", 1, nodesApps.getLength());
+    NodeList nodes = dom.getElementsByTagName("node");
+    assertEquals("incorrect number of elements", 2, nodes.getLength());
+    rm.stop();
+  }
+
+  public void verifyNodesXML(NodeList nodes, MockNM nm) throws JSONException,
+      Exception {
+    for (int i = 0; i < nodes.getLength(); i++) {
+      Element element = (Element) nodes.item(i);
+      verifyNodeInfoGeneric(nm,
+          WebServicesTestUtils.getXmlString(element, "state"),
+          WebServicesTestUtils.getXmlString(element, "rack"),
+          WebServicesTestUtils.getXmlString(element, "healthStatus"),
+          WebServicesTestUtils.getXmlString(element, "id"),
+          WebServicesTestUtils.getXmlString(element, "nodeHostName"),
+          WebServicesTestUtils.getXmlString(element, "nodeHTTPAddress"),
+          WebServicesTestUtils.getXmlLong(element, "lastHealthUpdate"),
+          WebServicesTestUtils.getXmlString(element, "healthReport"),
+          WebServicesTestUtils.getXmlInt(element, "numContainers"),
+          WebServicesTestUtils.getXmlLong(element, "usedMemoryMB"),
+          WebServicesTestUtils.getXmlLong(element, "availMemoryMB"));
+    }
+  }
+
+  public void verifyNodeInfo(JSONObject nodeInfo, MockNM nm)
+      throws JSONException, Exception {
+    assertEquals("incorrect number of elements", 11, nodeInfo.length());
+
+    verifyNodeInfoGeneric(nm, nodeInfo.getString("state"),
+        nodeInfo.getString("rack"), nodeInfo.getString("healthStatus"),
+        nodeInfo.getString("id"), nodeInfo.getString("nodeHostName"),
+        nodeInfo.getString("nodeHTTPAddress"),
+        nodeInfo.getLong("lastHealthUpdate"),
+        nodeInfo.getString("healthReport"), nodeInfo.getInt("numContainers"),
+        nodeInfo.getLong("usedMemoryMB"), nodeInfo.getLong("availMemoryMB"));
+
+  }
+
+  public void verifyNodeInfoGeneric(MockNM nm, String state, String rack,
+      String healthStatus, String id, String nodeHostName,
+      String nodeHTTPAddress, long lastHealthUpdate, String healthReport,
+      int numContainers, long usedMemoryMB, long availMemoryMB)
+      throws JSONException, Exception {
+
+    RMNode node = rm.getRMContext().getRMNodes().get(nm.getNodeId());
+    NodeHealthStatus health = node.getNodeHealthStatus();
+    ResourceScheduler sched = rm.getResourceScheduler();
+    SchedulerNodeReport report = sched.getNodeReport(nm.getNodeId());
+
+    WebServicesTestUtils.checkStringMatch("state", node.getState().toString(),
+        state);
+    WebServicesTestUtils.checkStringMatch("rack", node.getRackName(), rack);
+    WebServicesTestUtils.checkStringMatch("healthStatus", "Healthy",
+        healthStatus);
+    WebServicesTestUtils.checkStringMatch("id", nm.getNodeId().toString(), id);
+    WebServicesTestUtils.checkStringMatch("nodeHostName", nm.getNodeId()
+        .getHost(), nodeHostName);
+    WebServicesTestUtils.checkStringMatch("healthReport",
+        String.valueOf(health.getHealthReport()), healthReport);
+    String expectedHttpAddress = nm.getNodeId().getHost() + ":"
+        + nm.getHttpPort();
+    WebServicesTestUtils.checkStringMatch("nodeHTTPAddress",
+        expectedHttpAddress, nodeHTTPAddress);
+
+    long expectedHealthUpdate = health.getLastHealthReportTime();
+    assertEquals("lastHealthUpdate doesn't match, got: " + lastHealthUpdate
+        + " expected: " + expectedHealthUpdate, expectedHealthUpdate,
+        lastHealthUpdate);
+
+    if (report != null) {
+      assertEquals("numContainers doesn't match: " + numContainers,
+          report.getNumContainers(), numContainers);
+      assertEquals("usedMemoryMB doesn't match: " + usedMemoryMB, report
+          .getUsedResource().getMemory(), usedMemoryMB);
+      assertEquals("availMemoryMB doesn't match: " + availMemoryMB, report
+          .getAvailableResource().getMemory(), availMemoryMB);
+    }
+  }
+
+}