Return-Path: X-Original-To: apmail-hadoop-mapreduce-commits-archive@minotaur.apache.org Delivered-To: apmail-hadoop-mapreduce-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 23FB294D4 for ; Thu, 5 Jan 2012 20:02:01 +0000 (UTC) Received: (qmail 60449 invoked by uid 500); 5 Jan 2012 20:02:00 -0000 Delivered-To: apmail-hadoop-mapreduce-commits-archive@hadoop.apache.org Received: (qmail 60392 invoked by uid 500); 5 Jan 2012 20:02:00 -0000 Mailing-List: contact mapreduce-commits-help@hadoop.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: mapreduce-dev@hadoop.apache.org Delivered-To: mailing list mapreduce-commits@hadoop.apache.org Received: (qmail 60379 invoked by uid 99); 5 Jan 2012 20:02:00 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 05 Jan 2012 20:02:00 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 05 Jan 2012 20:01:46 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id A97932388A32; Thu, 5 Jan 2012 20:01:23 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1227801 [3/6] - in /hadoop/common/trunk/hadoop-mapreduce-project: ./ hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/ hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/... Date: Thu, 05 Jan 2012 20:01:21 -0000 To: mapreduce-commits@hadoop.apache.org From: acmurthy@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20120105200123.A97932388A32@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Added: hadoop/common/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/webapp/TestAMWebServicesTasks.java URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/webapp/TestAMWebServicesTasks.java?rev=1227801&view=auto ============================================================================== --- hadoop/common/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/webapp/TestAMWebServicesTasks.java (added) +++ hadoop/common/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/webapp/TestAMWebServicesTasks.java Thu Jan 5 20:01:20 2012 @@ -0,0 +1,821 @@ +/** + * 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.mapreduce.v2.app.webapp; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.StringReader; +import java.util.Map; + +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.mapreduce.v2.api.records.JobId; +import org.apache.hadoop.mapreduce.v2.api.records.TaskId; +import org.apache.hadoop.mapreduce.v2.api.records.TaskReport; +import org.apache.hadoop.mapreduce.v2.app.AppContext; +import org.apache.hadoop.mapreduce.v2.app.MockJobs; +import org.apache.hadoop.mapreduce.v2.app.job.Job; +import org.apache.hadoop.mapreduce.v2.app.job.Task; +import org.apache.hadoop.mapreduce.v2.util.MRApps; +import org.apache.hadoop.yarn.Clock; +import org.apache.hadoop.yarn.api.records.ApplicationAttemptId; +import org.apache.hadoop.yarn.api.records.ApplicationId; +import org.apache.hadoop.yarn.event.EventHandler; +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; + +/** + * Test the app master web service Rest API for getting tasks, a specific task, + * and task counters. + * + * /ws/v1/mapreduce/jobs/{jobid}/tasks + * /ws/v1/mapreduce/jobs/{jobid}/tasks/{taskid} + * /ws/v1/mapreduce/jobs/{jobid}/tasks/{taskid}/counters + */ +public class TestAMWebServicesTasks extends JerseyTest { + + private static Configuration conf = new Configuration(); + private static TestAppContext appContext; + + static class TestAppContext implements AppContext { + final ApplicationAttemptId appAttemptID; + final ApplicationId appID; + final String user = MockJobs.newUserName(); + final Map jobs; + final long startTime = System.currentTimeMillis(); + + TestAppContext(int appid, int numJobs, int numTasks, int numAttempts) { + appID = MockJobs.newAppID(appid); + appAttemptID = MockJobs.newAppAttemptID(appID, 0); + jobs = MockJobs.newJobs(appID, numJobs, numTasks, numAttempts); + } + + TestAppContext() { + this(0, 1, 2, 1); + } + + @Override + public ApplicationAttemptId getApplicationAttemptId() { + return appAttemptID; + } + + @Override + public ApplicationId getApplicationID() { + return appID; + } + + @Override + public CharSequence getUser() { + return user; + } + + @Override + public Job getJob(JobId jobID) { + return jobs.get(jobID); + } + + @Override + public Map getAllJobs() { + return jobs; // OK + } + + @SuppressWarnings("rawtypes") + @Override + public EventHandler getEventHandler() { + return null; + } + + @Override + public Clock getClock() { + return null; + } + + @Override + public String getApplicationName() { + return "TestApp"; + } + + @Override + public long getStartTime() { + return startTime; + } + } + + private Injector injector = Guice.createInjector(new ServletModule() { + @Override + protected void configureServlets() { + + appContext = new TestAppContext(); + bind(JAXBContextResolver.class); + bind(AMWebServices.class); + bind(GenericExceptionHandler.class); + bind(AppContext.class).toInstance(appContext); + bind(Configuration.class).toInstance(conf); + + 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 TestAMWebServicesTasks() { + super(new WebAppDescriptor.Builder( + "org.apache.hadoop.mapreduce.v2.app.webapp") + .contextListenerClass(GuiceServletConfig.class) + .filterClass(com.google.inject.servlet.GuiceFilter.class) + .contextPath("jersey-guice-filter").servletPath("/").build()); + } + + @Test + public void testTasks() throws JSONException, Exception { + WebResource r = resource(); + Map jobsMap = appContext.getAllJobs(); + for (JobId id : jobsMap.keySet()) { + String jobId = MRApps.toString(id); + ClientResponse response = r.path("ws").path("v1").path("mapreduce") + .path("jobs").path(jobId).path("tasks") + .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 tasks = json.getJSONObject("tasks"); + JSONArray arr = tasks.getJSONArray("task"); + assertEquals("incorrect number of elements", 2, arr.length()); + + verifyAMTask(arr, jobsMap.get(id), null); + } + } + + @Test + public void testTasksDefault() throws JSONException, Exception { + WebResource r = resource(); + Map jobsMap = appContext.getAllJobs(); + for (JobId id : jobsMap.keySet()) { + String jobId = MRApps.toString(id); + ClientResponse response = r.path("ws").path("v1").path("mapreduce") + .path("jobs").path(jobId).path("tasks").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 tasks = json.getJSONObject("tasks"); + JSONArray arr = tasks.getJSONArray("task"); + assertEquals("incorrect number of elements", 2, arr.length()); + + verifyAMTask(arr, jobsMap.get(id), null); + } + } + + @Test + public void testTasksSlash() throws JSONException, Exception { + WebResource r = resource(); + Map jobsMap = appContext.getAllJobs(); + for (JobId id : jobsMap.keySet()) { + String jobId = MRApps.toString(id); + ClientResponse response = r.path("ws").path("v1").path("mapreduce") + .path("jobs").path(jobId).path("tasks/") + .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 tasks = json.getJSONObject("tasks"); + JSONArray arr = tasks.getJSONArray("task"); + assertEquals("incorrect number of elements", 2, arr.length()); + + verifyAMTask(arr, jobsMap.get(id), null); + } + } + + @Test + public void testTasksXML() throws JSONException, Exception { + + WebResource r = resource(); + Map jobsMap = appContext.getAllJobs(); + for (JobId id : jobsMap.keySet()) { + String jobId = MRApps.toString(id); + ClientResponse response = r.path("ws").path("v1").path("mapreduce") + .path("jobs").path(jobId).path("tasks") + .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 tasks = dom.getElementsByTagName("tasks"); + assertEquals("incorrect number of elements", 1, tasks.getLength()); + NodeList task = dom.getElementsByTagName("task"); + verifyAMTaskXML(task, jobsMap.get(id)); + } + } + + @Test + public void testTasksQueryMap() throws JSONException, Exception { + WebResource r = resource(); + Map jobsMap = appContext.getAllJobs(); + for (JobId id : jobsMap.keySet()) { + String jobId = MRApps.toString(id); + String type = "m"; + ClientResponse response = r.path("ws").path("v1").path("mapreduce") + .path("jobs").path(jobId).path("tasks").queryParam("type", type) + .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 tasks = json.getJSONObject("tasks"); + JSONArray arr = tasks.getJSONArray("task"); + assertEquals("incorrect number of elements", 1, arr.length()); + verifyAMTask(arr, jobsMap.get(id), type); + } + } + + @Test + public void testTasksQueryReduce() throws JSONException, Exception { + WebResource r = resource(); + Map jobsMap = appContext.getAllJobs(); + for (JobId id : jobsMap.keySet()) { + String jobId = MRApps.toString(id); + String type = "r"; + ClientResponse response = r.path("ws").path("v1").path("mapreduce") + .path("jobs").path(jobId).path("tasks").queryParam("type", type) + .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 tasks = json.getJSONObject("tasks"); + JSONArray arr = tasks.getJSONArray("task"); + assertEquals("incorrect number of elements", 1, arr.length()); + verifyAMTask(arr, jobsMap.get(id), type); + } + } + + @Test + public void testTasksQueryInvalid() throws JSONException, Exception { + WebResource r = resource(); + Map jobsMap = appContext.getAllJobs(); + for (JobId id : jobsMap.keySet()) { + String jobId = MRApps.toString(id); + // tasktype must be exactly either "m" or "r" + String tasktype = "reduce"; + + try { + r.path("ws").path("v1").path("mapreduce").path("jobs").path(jobId) + .path("tasks").queryParam("type", tasktype) + .accept(MediaType.APPLICATION_JSON).get(JSONObject.class); + fail("should have thrown exception on invalid uri"); + } 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: tasktype must be either m or r", message); + WebServicesTestUtils.checkStringMatch("exception type", + "BadRequestException", type); + WebServicesTestUtils.checkStringMatch("exception classname", + "org.apache.hadoop.yarn.webapp.BadRequestException", classname); + } + } + } + + @Test + public void testTaskId() throws JSONException, Exception { + WebResource r = resource(); + Map jobsMap = appContext.getAllJobs(); + for (JobId id : jobsMap.keySet()) { + String jobId = MRApps.toString(id); + for (Task task : jobsMap.get(id).getTasks().values()) { + + String tid = MRApps.toString(task.getID()); + ClientResponse response = r.path("ws").path("v1").path("mapreduce") + .path("jobs").path(jobId).path("tasks").path(tid) + .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 info = json.getJSONObject("task"); + verifyAMSingleTask(info, task); + } + } + } + + @Test + public void testTaskIdSlash() throws JSONException, Exception { + WebResource r = resource(); + Map jobsMap = appContext.getAllJobs(); + for (JobId id : jobsMap.keySet()) { + String jobId = MRApps.toString(id); + for (Task task : jobsMap.get(id).getTasks().values()) { + + String tid = MRApps.toString(task.getID()); + ClientResponse response = r.path("ws").path("v1").path("mapreduce") + .path("jobs").path(jobId).path("tasks").path(tid + "/") + .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 info = json.getJSONObject("task"); + verifyAMSingleTask(info, task); + } + } + } + + @Test + public void testTaskIdDefault() throws JSONException, Exception { + WebResource r = resource(); + Map jobsMap = appContext.getAllJobs(); + for (JobId id : jobsMap.keySet()) { + String jobId = MRApps.toString(id); + for (Task task : jobsMap.get(id).getTasks().values()) { + + String tid = MRApps.toString(task.getID()); + ClientResponse response = r.path("ws").path("v1").path("mapreduce") + .path("jobs").path(jobId).path("tasks").path(tid) + .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("task"); + verifyAMSingleTask(info, task); + } + } + } + + @Test + public void testTaskIdBogus() throws JSONException, Exception { + WebResource r = resource(); + Map jobsMap = appContext.getAllJobs(); + for (JobId id : jobsMap.keySet()) { + String jobId = MRApps.toString(id); + String tid = "bogustaskid"; + try { + r.path("ws").path("v1").path("mapreduce").path("jobs").path(jobId) + .path("tasks").path(tid).get(JSONObject.class); + fail("should have thrown exception on invalid uri"); + } 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: Error parsing task ID: bogustaskid", message); + WebServicesTestUtils.checkStringMatch("exception type", + "NotFoundException", type); + WebServicesTestUtils.checkStringMatch("exception classname", + "org.apache.hadoop.yarn.webapp.NotFoundException", classname); + } + } + } + + @Test + public void testTaskIdNonExist() throws JSONException, Exception { + WebResource r = resource(); + Map jobsMap = appContext.getAllJobs(); + for (JobId id : jobsMap.keySet()) { + String jobId = MRApps.toString(id); + String tid = "task_1234_0_0_m_0"; + try { + r.path("ws").path("v1").path("mapreduce").path("jobs").path(jobId) + .path("tasks").path(tid).get(JSONObject.class); + fail("should have thrown exception on invalid uri"); + } 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: task not found with id task_1234_0_0_m_0", + message); + WebServicesTestUtils.checkStringMatch("exception type", + "NotFoundException", type); + WebServicesTestUtils.checkStringMatch("exception classname", + "org.apache.hadoop.yarn.webapp.NotFoundException", classname); + } + } + } + + @Test + public void testTaskIdInvalid() throws JSONException, Exception { + WebResource r = resource(); + Map jobsMap = appContext.getAllJobs(); + for (JobId id : jobsMap.keySet()) { + String jobId = MRApps.toString(id); + String tid = "task_1234_0_0_d_0"; + try { + r.path("ws").path("v1").path("mapreduce").path("jobs").path(jobId) + .path("tasks").path(tid).get(JSONObject.class); + fail("should have thrown exception on invalid uri"); + } 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: Unknown task symbol: d", message); + WebServicesTestUtils.checkStringMatch("exception type", + "NotFoundException", type); + WebServicesTestUtils.checkStringMatch("exception classname", + "org.apache.hadoop.yarn.webapp.NotFoundException", classname); + } + } + } + + @Test + public void testTaskIdInvalid2() throws JSONException, Exception { + WebResource r = resource(); + Map jobsMap = appContext.getAllJobs(); + for (JobId id : jobsMap.keySet()) { + String jobId = MRApps.toString(id); + String tid = "task_1234_0_m_0"; + try { + r.path("ws").path("v1").path("mapreduce").path("jobs").path(jobId) + .path("tasks").path(tid).get(JSONObject.class); + fail("should have thrown exception on invalid uri"); + } 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: For input string: \"m\"", message); + WebServicesTestUtils.checkStringMatch("exception type", + "NotFoundException", type); + WebServicesTestUtils.checkStringMatch("exception classname", + "org.apache.hadoop.yarn.webapp.NotFoundException", classname); + } + } + } + + @Test + public void testTaskIdInvalid3() throws JSONException, Exception { + WebResource r = resource(); + Map jobsMap = appContext.getAllJobs(); + for (JobId id : jobsMap.keySet()) { + String jobId = MRApps.toString(id); + String tid = "task_1234_0_0_m"; + try { + r.path("ws").path("v1").path("mapreduce").path("jobs").path(jobId) + .path("tasks").path(tid).get(JSONObject.class); + fail("should have thrown exception on invalid uri"); + } 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: Error parsing task ID: task_1234_0_0_m", + message); + WebServicesTestUtils.checkStringMatch("exception type", + "NotFoundException", type); + WebServicesTestUtils.checkStringMatch("exception classname", + "org.apache.hadoop.yarn.webapp.NotFoundException", classname); + } + } + } + + @Test + public void testTaskIdXML() throws JSONException, Exception { + WebResource r = resource(); + Map jobsMap = appContext.getAllJobs(); + for (JobId id : jobsMap.keySet()) { + String jobId = MRApps.toString(id); + for (Task task : jobsMap.get(id).getTasks().values()) { + + String tid = MRApps.toString(task.getID()); + ClientResponse response = r.path("ws").path("v1").path("mapreduce") + .path("jobs").path(jobId).path("tasks").path(tid) + .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("task"); + for (int i = 0; i < nodes.getLength(); i++) { + Element element = (Element) nodes.item(i); + verifyAMSingleTaskXML(element, task); + } + } + } + } + + public void verifyAMSingleTask(JSONObject info, Task task) + throws JSONException { + assertEquals("incorrect number of elements", 8, info.length()); + + verifyTaskGeneric(task, info.getString("id"), info.getString("state"), + info.getString("type"), info.getString("successfulAttempt"), + info.getLong("startTime"), info.getLong("finishTime"), + info.getLong("elapsedTime"), (float) info.getDouble("progress")); + } + + public void verifyAMTask(JSONArray arr, Job job, String type) + throws JSONException { + for (Task task : job.getTasks().values()) { + TaskId id = task.getID(); + String tid = MRApps.toString(id); + Boolean found = false; + if (type != null && task.getType() == MRApps.taskType(type)) { + + for (int i = 0; i < arr.length(); i++) { + JSONObject info = arr.getJSONObject(i); + if (tid.matches(info.getString("id"))) { + found = true; + verifyAMSingleTask(info, task); + } + } + assertTrue("task with id: " + tid + " not in web service output", found); + } + } + } + + public void verifyTaskGeneric(Task task, String id, String state, + String type, String successfulAttempt, long startTime, long finishTime, + long elapsedTime, float progress) { + + TaskId taskid = task.getID(); + String tid = MRApps.toString(taskid); + TaskReport report = task.getReport(); + + WebServicesTestUtils.checkStringMatch("id", tid, id); + WebServicesTestUtils.checkStringMatch("type", task.getType().toString(), + type); + WebServicesTestUtils.checkStringMatch("state", report.getTaskState() + .toString(), state); + // not easily checked without duplicating logic, just make sure its here + assertNotNull("successfulAttempt null", successfulAttempt); + assertEquals("startTime wrong", report.getStartTime(), startTime); + assertEquals("finishTime wrong", report.getFinishTime(), finishTime); + assertEquals("elapsedTime wrong", finishTime - startTime, elapsedTime); + assertEquals("progress wrong", report.getProgress() * 100, progress, 1e-3f); + } + + public void verifyAMSingleTaskXML(Element element, Task task) { + verifyTaskGeneric(task, WebServicesTestUtils.getXmlString(element, "id"), + WebServicesTestUtils.getXmlString(element, "state"), + WebServicesTestUtils.getXmlString(element, "type"), + WebServicesTestUtils.getXmlString(element, "successfulAttempt"), + WebServicesTestUtils.getXmlLong(element, "startTime"), + WebServicesTestUtils.getXmlLong(element, "finishTime"), + WebServicesTestUtils.getXmlLong(element, "elapsedTime"), + WebServicesTestUtils.getXmlFloat(element, "progress")); + } + + public void verifyAMTaskXML(NodeList nodes, Job job) { + + assertEquals("incorrect number of elements", 2, nodes.getLength()); + + for (Task task : job.getTasks().values()) { + TaskId id = task.getID(); + String tid = MRApps.toString(id); + Boolean found = false; + for (int i = 0; i < nodes.getLength(); i++) { + Element element = (Element) nodes.item(i); + + if (tid.matches(WebServicesTestUtils.getXmlString(element, "id"))) { + found = true; + verifyAMSingleTaskXML(element, task); + } + } + assertTrue("task with id: " + tid + " not in web service output", found); + } + } + + @Test + public void testTaskIdCounters() throws JSONException, Exception { + WebResource r = resource(); + Map jobsMap = appContext.getAllJobs(); + for (JobId id : jobsMap.keySet()) { + String jobId = MRApps.toString(id); + for (Task task : jobsMap.get(id).getTasks().values()) { + + String tid = MRApps.toString(task.getID()); + ClientResponse response = r.path("ws").path("v1").path("mapreduce") + .path("jobs").path(jobId).path("tasks").path(tid).path("counters") + .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 info = json.getJSONObject("jobTaskCounters"); + verifyAMJobTaskCounters(info, task); + } + } + } + + @Test + public void testTaskIdCountersSlash() throws JSONException, Exception { + WebResource r = resource(); + Map jobsMap = appContext.getAllJobs(); + for (JobId id : jobsMap.keySet()) { + String jobId = MRApps.toString(id); + for (Task task : jobsMap.get(id).getTasks().values()) { + + String tid = MRApps.toString(task.getID()); + ClientResponse response = r.path("ws").path("v1").path("mapreduce") + .path("jobs").path(jobId).path("tasks").path(tid).path("counters/") + .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 info = json.getJSONObject("jobTaskCounters"); + verifyAMJobTaskCounters(info, task); + } + } + } + + @Test + public void testTaskIdCountersDefault() throws JSONException, Exception { + WebResource r = resource(); + Map jobsMap = appContext.getAllJobs(); + for (JobId id : jobsMap.keySet()) { + String jobId = MRApps.toString(id); + for (Task task : jobsMap.get(id).getTasks().values()) { + + String tid = MRApps.toString(task.getID()); + ClientResponse response = r.path("ws").path("v1").path("mapreduce") + .path("jobs").path(jobId).path("tasks").path(tid).path("counters") + .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("jobTaskCounters"); + verifyAMJobTaskCounters(info, task); + } + } + } + + @Test + public void testJobTaskCountersXML() throws Exception { + WebResource r = resource(); + Map jobsMap = appContext.getAllJobs(); + for (JobId id : jobsMap.keySet()) { + String jobId = MRApps.toString(id); + for (Task task : jobsMap.get(id).getTasks().values()) { + + String tid = MRApps.toString(task.getID()); + ClientResponse response = r.path("ws").path("v1").path("mapreduce") + .path("jobs").path(jobId).path("tasks").path(tid).path("counters") + .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 info = dom.getElementsByTagName("jobTaskCounters"); + verifyAMTaskCountersXML(info, task); + } + } + } + + public void verifyAMJobTaskCounters(JSONObject info, Task task) + throws JSONException { + + assertEquals("incorrect number of elements", 2, info.length()); + + WebServicesTestUtils.checkStringMatch("id", MRApps.toString(task.getID()), + info.getString("id")); + // just do simple verification of fields - not data is correct + // in the fields + JSONArray counterGroups = info.getJSONArray("taskCounterGroup"); + for (int i = 0; i < counterGroups.length(); i++) { + JSONObject counterGroup = counterGroups.getJSONObject(i); + String name = counterGroup.getString("counterGroupName"); + assertTrue("name not set", (name != null && !name.isEmpty())); + JSONArray counters = counterGroup.getJSONArray("counter"); + for (int j = 0; j < counters.length(); j++) { + JSONObject counter = counters.getJSONObject(i); + String counterName = counter.getString("name"); + assertTrue("name not set", + (counterName != null && !counterName.isEmpty())); + long value = counter.getLong("value"); + assertTrue("value >= 0", value >= 0); + } + } + } + + public void verifyAMTaskCountersXML(NodeList nodes, Task task) { + + for (int i = 0; i < nodes.getLength(); i++) { + + Element element = (Element) nodes.item(i); + WebServicesTestUtils.checkStringMatch("id", + MRApps.toString(task.getID()), + WebServicesTestUtils.getXmlString(element, "id")); + // just do simple verification of fields - not data is correct + // in the fields + NodeList groups = element.getElementsByTagName("taskCounterGroup"); + + for (int j = 0; j < groups.getLength(); j++) { + Element counters = (Element) groups.item(j); + assertNotNull("should have counters in the web service info", counters); + String name = WebServicesTestUtils.getXmlString(counters, + "counterGroupName"); + assertTrue("name not set", (name != null && !name.isEmpty())); + NodeList counterArr = counters.getElementsByTagName("counter"); + for (int z = 0; z < counterArr.getLength(); z++) { + Element counter = (Element) counterArr.item(z); + String counterName = WebServicesTestUtils.getXmlString(counter, + "name"); + assertTrue("counter name not set", + (counterName != null && !counterName.isEmpty())); + + long value = WebServicesTestUtils.getXmlLong(counter, "value"); + assertTrue("value not >= 0", value >= 0); + + } + } + } + } + +} Modified: hadoop/common/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsWebServices.java URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsWebServices.java?rev=1227801&r1=1227800&r2=1227801&view=diff ============================================================================== --- hadoop/common/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsWebServices.java (original) +++ hadoop/common/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsWebServices.java Thu Jan 5 20:01:20 2012 @@ -31,14 +31,13 @@ import javax.ws.rs.core.UriInfo; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.mapreduce.v2.api.records.AMInfo; -import org.apache.hadoop.mapreduce.v2.api.records.JobId; -import org.apache.hadoop.mapreduce.v2.api.records.TaskAttemptId; import org.apache.hadoop.mapreduce.v2.api.records.TaskId; import org.apache.hadoop.mapreduce.v2.api.records.TaskType; import org.apache.hadoop.mapreduce.v2.app.AppContext; import org.apache.hadoop.mapreduce.v2.app.job.Job; import org.apache.hadoop.mapreduce.v2.app.job.Task; import org.apache.hadoop.mapreduce.v2.app.job.TaskAttempt; +import org.apache.hadoop.mapreduce.v2.app.webapp.AMWebServices; import org.apache.hadoop.mapreduce.v2.app.webapp.dao.ConfInfo; import org.apache.hadoop.mapreduce.v2.app.webapp.dao.JobCounterInfo; import org.apache.hadoop.mapreduce.v2.app.webapp.dao.JobTaskAttemptCounterInfo; @@ -131,7 +130,7 @@ public class HsWebServices { try { sBegin = Long.parseLong(startedBegin); } catch (NumberFormatException e) { - throw new BadRequestException(e.getMessage()); + throw new BadRequestException("Invalid number format: " + e.getMessage()); } if (sBegin < 0) { throw new BadRequestException("startedTimeBegin must be greater than 0"); @@ -142,7 +141,7 @@ public class HsWebServices { try { sEnd = Long.parseLong(startedEnd); } catch (NumberFormatException e) { - throw new BadRequestException(e.getMessage()); + throw new BadRequestException("Invalid number format: " + e.getMessage()); } if (sEnd < 0) { throw new BadRequestException("startedTimeEnd must be greater than 0"); @@ -158,10 +157,10 @@ public class HsWebServices { try { fBegin = Long.parseLong(finishBegin); } catch (NumberFormatException e) { - throw new BadRequestException(e.getMessage()); + throw new BadRequestException("Invalid number format: " + e.getMessage()); } if (fBegin < 0) { - throw new BadRequestException("finishTimeBegin must be greater than 0"); + throw new BadRequestException("finishedTimeBegin must be greater than 0"); } } if (finishEnd != null && !finishEnd.isEmpty()) { @@ -169,15 +168,15 @@ public class HsWebServices { try { fEnd = Long.parseLong(finishEnd); } catch (NumberFormatException e) { - throw new BadRequestException(e.getMessage()); + throw new BadRequestException("Invalid number format: " + e.getMessage()); } if (fEnd < 0) { - throw new BadRequestException("finishTimeEnd must be greater than 0"); + throw new BadRequestException("finishedTimeEnd must be greater than 0"); } } if (fBegin > fEnd) { throw new BadRequestException( - "finishTimeEnd must be greater than finishTimeBegin"); + "finishedTimeEnd must be greater than finishedTimeBegin"); } for (Job job : appCtx.getAllJobs().values()) { @@ -200,7 +199,7 @@ public class HsWebServices { } if (userQuery != null && !userQuery.isEmpty()) { - if (!jobInfo.getName().equals(userQuery)) { + if (!jobInfo.getUserName().equals(userQuery)) { continue; } } @@ -224,14 +223,8 @@ public class HsWebServices { @Path("/mapreduce/jobs/{jobid}") @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) public JobInfo getJob(@PathParam("jobid") String jid) { - JobId jobId = MRApps.toJobID(jid); - if (jobId == null) { - throw new NotFoundException("job, " + jid + ", is not found"); - } - Job job = appCtx.getJob(jobId); - if (job == null) { - throw new NotFoundException("job, " + jid + ", is not found"); - } + + Job job = AMWebServices.getJobFromJobIdString(jid, appCtx); return new JobInfo(job); } @@ -239,14 +232,8 @@ public class HsWebServices { @Path("/mapreduce/jobs/{jobid}/attempts") @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) public AMAttemptsInfo getJobAttempts(@PathParam("jobid") String jid) { - JobId jobId = MRApps.toJobID(jid); - if (jobId == null) { - throw new NotFoundException("job, " + jid + ", is not found"); - } - Job job = appCtx.getJob(jobId); - if (job == null) { - throw new NotFoundException("job, " + jid + ", is not found"); - } + + Job job = AMWebServices.getJobFromJobIdString(jid, appCtx); AMAttemptsInfo amAttempts = new AMAttemptsInfo(); for (AMInfo amInfo : job.getAMInfos()) { AMAttemptInfo attempt = new AMAttemptInfo(amInfo, MRApps.toString(job @@ -261,53 +248,17 @@ public class HsWebServices { @Path("/mapreduce/jobs/{jobid}/counters") @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) public JobCounterInfo getJobCounters(@PathParam("jobid") String jid) { - JobId jobId = MRApps.toJobID(jid); - if (jobId == null) { - throw new NotFoundException("job, " + jid + ", is not found"); - } - Job job = appCtx.getJob(jobId); - if (job == null) { - throw new NotFoundException("job, " + jid + ", is not found"); - } - return new JobCounterInfo(this.appCtx, job); - } - @GET - @Path("/mapreduce/jobs/{jobid}/tasks/{taskid}/counters") - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public JobTaskCounterInfo getSingleTaskCounters( - @PathParam("jobid") String jid, @PathParam("taskid") String tid) { - JobId jobId = MRApps.toJobID(jid); - if (jobId == null) { - throw new NotFoundException("job, " + jid + ", is not found"); - } - Job job = this.appCtx.getJob(jobId); - if (job == null) { - throw new NotFoundException("job, " + jid + ", is not found"); - } - TaskId taskID = MRApps.toTaskID(tid); - if (taskID == null) { - throw new NotFoundException("taskid " + tid + " not found or invalid"); - } - Task task = job.getTask(taskID); - if (task == null) { - throw new NotFoundException("task not found with id " + tid); - } - return new JobTaskCounterInfo(task); + Job job = AMWebServices.getJobFromJobIdString(jid, appCtx); + return new JobCounterInfo(this.appCtx, job); } @GET @Path("/mapreduce/jobs/{jobid}/conf") @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) public ConfInfo getJobConf(@PathParam("jobid") String jid) { - JobId jobId = MRApps.toJobID(jid); - if (jobId == null) { - throw new NotFoundException("job, " + jid + ", is not found"); - } - Job job = appCtx.getJob(jobId); - if (job == null) { - throw new NotFoundException("job, " + jid + ", is not found"); - } + + Job job = AMWebServices.getJobFromJobIdString(jid, appCtx); ConfInfo info; try { info = new ConfInfo(job, this.conf); @@ -315,7 +266,6 @@ public class HsWebServices { throw new NotFoundException("unable to load configuration for job: " + jid); } - return info; } @@ -324,10 +274,8 @@ public class HsWebServices { @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) public TasksInfo getJobTasks(@PathParam("jobid") String jid, @QueryParam("type") String type) { - Job job = this.appCtx.getJob(MRApps.toJobID(jid)); - if (job == null) { - throw new NotFoundException("job, " + jid + ", is not found"); - } + + Job job = AMWebServices.getJobFromJobIdString(jid, appCtx); TasksInfo allTasks = new TasksInfo(); for (Task task : job.getTasks().values()) { TaskType ttype = null; @@ -351,10 +299,20 @@ public class HsWebServices { @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) public TaskInfo getJobTask(@PathParam("jobid") String jid, @PathParam("taskid") String tid) { - Job job = this.appCtx.getJob(MRApps.toJobID(jid)); - if (job == null) { - throw new NotFoundException("job, " + jid + ", is not found"); - } + + Job job = AMWebServices.getJobFromJobIdString(jid, appCtx); + Task task = AMWebServices.getTaskFromTaskIdString(tid, job); + return new TaskInfo(task); + + } + + @GET + @Path("/mapreduce/jobs/{jobid}/tasks/{taskid}/counters") + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public JobTaskCounterInfo getSingleTaskCounters( + @PathParam("jobid") String jid, @PathParam("taskid") String tid) { + + Job job = AMWebServices.getJobFromJobIdString(jid, appCtx); TaskId taskID = MRApps.toTaskID(tid); if (taskID == null) { throw new NotFoundException("taskid " + tid + " not found or invalid"); @@ -363,8 +321,7 @@ public class HsWebServices { if (task == null) { throw new NotFoundException("task not found with id " + tid); } - return new TaskInfo(task); - + return new JobTaskCounterInfo(task); } @GET @@ -372,19 +329,10 @@ public class HsWebServices { @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) public TaskAttemptsInfo getJobTaskAttempts(@PathParam("jobid") String jid, @PathParam("taskid") String tid) { + TaskAttemptsInfo attempts = new TaskAttemptsInfo(); - Job job = this.appCtx.getJob(MRApps.toJobID(jid)); - if (job == null) { - throw new NotFoundException("job, " + jid + ", is not found"); - } - TaskId taskID = MRApps.toTaskID(tid); - if (taskID == null) { - throw new NotFoundException("taskid " + tid + " not found or invalid"); - } - Task task = job.getTask(taskID); - if (task == null) { - throw new NotFoundException("task not found with id " + tid); - } + Job job = AMWebServices.getJobFromJobIdString(jid, appCtx); + Task task = AMWebServices.getTaskFromTaskIdString(tid, job); for (TaskAttempt ta : task.getAttempts().values()) { if (ta != null) { if (task.getType() == TaskType.REDUCE) { @@ -402,28 +350,11 @@ public class HsWebServices { @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) public TaskAttemptInfo getJobTaskAttemptId(@PathParam("jobid") String jid, @PathParam("taskid") String tid, @PathParam("attemptid") String attId) { - Job job = this.appCtx.getJob(MRApps.toJobID(jid)); - if (job == null) { - throw new NotFoundException("job, " + jid + ", is not found"); - } - TaskId taskID = MRApps.toTaskID(tid); - if (taskID == null) { - throw new NotFoundException("taskid " + tid + " not found or invalid"); - } - Task task = job.getTask(taskID); - if (task == null) { - throw new NotFoundException("task not found with id " + tid); - } - TaskAttemptId attemptId = MRApps.toTaskAttemptID(attId); - if (attemptId == null) { - throw new NotFoundException("task attempt id " + attId - + " not found or invalid"); - } - TaskAttempt ta = task.getAttempt(attemptId); - if (ta == null) { - throw new NotFoundException("Error getting info on task attempt id " - + attId); - } + + Job job = AMWebServices.getJobFromJobIdString(jid, appCtx); + Task task = AMWebServices.getTaskFromTaskIdString(tid, job); + TaskAttempt ta = AMWebServices.getTaskAttemptFromTaskAttemptString(attId, + task); if (task.getType() == TaskType.REDUCE) { return new ReduceTaskAttemptInfo(ta, task.getType()); } else { @@ -437,32 +368,11 @@ public class HsWebServices { public JobTaskAttemptCounterInfo getJobTaskAttemptIdCounters( @PathParam("jobid") String jid, @PathParam("taskid") String tid, @PathParam("attemptid") String attId) { - JobId jobId = MRApps.toJobID(jid); - if (jobId == null) { - throw new NotFoundException("job, " + jid + ", is not found"); - } - Job job = this.appCtx.getJob(jobId); - if (job == null) { - throw new NotFoundException("job, " + jid + ", is not found"); - } - TaskId taskID = MRApps.toTaskID(tid); - if (taskID == null) { - throw new NotFoundException("taskid " + tid + " not found or invalid"); - } - Task task = job.getTask(taskID); - if (task == null) { - throw new NotFoundException("task not found with id " + tid); - } - TaskAttemptId attemptId = MRApps.toTaskAttemptID(attId); - if (attemptId == null) { - throw new NotFoundException("task attempt id " + attId - + " not found or invalid"); - } - TaskAttempt ta = task.getAttempt(attemptId); - if (ta == null) { - throw new NotFoundException("Error getting info on task attempt id " - + attId); - } + + Job job = AMWebServices.getJobFromJobIdString(jid, appCtx); + Task task = AMWebServices.getTaskFromTaskIdString(tid, job); + TaskAttempt ta = AMWebServices.getTaskAttemptFromTaskAttemptString(attId, + task); return new JobTaskAttemptCounterInfo(ta); } Modified: hadoop/common/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/JAXBContextResolver.java URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/JAXBContextResolver.java?rev=1227801&r1=1227800&r2=1227801&view=diff ============================================================================== --- hadoop/common/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/JAXBContextResolver.java (original) +++ hadoop/common/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/JAXBContextResolver.java Thu Jan 5 20:01:20 2012 @@ -42,6 +42,8 @@ import org.apache.hadoop.mapreduce.v2.ap import org.apache.hadoop.mapreduce.v2.app.webapp.dao.TaskCounterGroupInfo; import org.apache.hadoop.mapreduce.v2.app.webapp.dao.TaskCounterInfo; import org.apache.hadoop.mapreduce.v2.app.webapp.dao.TasksInfo; +import org.apache.hadoop.mapreduce.v2.app.webapp.dao.TaskInfo; + import org.apache.hadoop.mapreduce.v2.hs.webapp.dao.AMAttemptInfo; import org.apache.hadoop.mapreduce.v2.hs.webapp.dao.AMAttemptsInfo; import org.apache.hadoop.mapreduce.v2.hs.webapp.dao.HistoryInfo; @@ -57,13 +59,12 @@ public class JAXBContextResolver impleme // you have to specify all the dao classes here private final Class[] cTypes = { HistoryInfo.class, JobInfo.class, - JobsInfo.class, TasksInfo.class, TaskAttemptsInfo.class, ConfInfo.class, - CounterInfo.class, JobTaskCounterInfo.class, - JobTaskAttemptCounterInfo.class, - TaskCounterInfo.class, JobCounterInfo.class, ReduceTaskAttemptInfo.class, - TaskAttemptInfo.class, TaskAttemptsInfo.class, CounterGroupInfo.class, - TaskCounterGroupInfo.class, - AMAttemptInfo.class, AMAttemptsInfo.class}; + JobsInfo.class, TaskInfo.class, TasksInfo.class, TaskAttemptsInfo.class, + ConfInfo.class, CounterInfo.class, JobTaskCounterInfo.class, + JobTaskAttemptCounterInfo.class, TaskCounterInfo.class, + JobCounterInfo.class, ReduceTaskAttemptInfo.class, TaskAttemptInfo.class, + TaskAttemptsInfo.class, CounterGroupInfo.class, + TaskCounterGroupInfo.class, AMAttemptInfo.class, AMAttemptsInfo.class }; public JAXBContextResolver() throws Exception { this.types = new HashSet(Arrays.asList(cTypes)); Modified: hadoop/common/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/dao/AMAttemptInfo.java URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/dao/AMAttemptInfo.java?rev=1227801&r1=1227800&r2=1227801&view=diff ============================================================================== --- hadoop/common/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/dao/AMAttemptInfo.java (original) +++ hadoop/common/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/dao/AMAttemptInfo.java Thu Jan 5 20:01:20 2012 @@ -26,6 +26,7 @@ import javax.xml.bind.annotation.XmlRoot import javax.xml.bind.annotation.XmlTransient; import org.apache.hadoop.mapreduce.v2.api.records.AMInfo; +import org.apache.hadoop.yarn.api.records.ContainerId; import org.apache.hadoop.yarn.api.records.NodeId; import org.apache.hadoop.yarn.util.BuilderUtils; @@ -48,21 +49,28 @@ public class AMAttemptInfo { public AMAttemptInfo(AMInfo amInfo, String jobId, String user, String host, String pathPrefix) { - this.nodeHttpAddress = amInfo.getNodeManagerHost() + ":" - + amInfo.getNodeManagerHttpPort(); - NodeId nodeId = BuilderUtils.newNodeId(amInfo.getNodeManagerHost(), - amInfo.getNodeManagerPort()); - this.nodeId = nodeId.toString(); + this.nodeHttpAddress = ""; + this.nodeId = ""; + String nmHost = amInfo.getNodeManagerHost(); + int nmPort = amInfo.getNodeManagerHttpPort(); + if (nmHost != null) { + this.nodeHttpAddress = nmHost + ":" + nmPort; + NodeId nodeId = BuilderUtils.newNodeId(nmHost, nmPort); + this.nodeId = nodeId.toString(); + } this.id = amInfo.getAppAttemptId().getAttemptId(); this.startTime = amInfo.getStartTime(); - this.containerId = amInfo.getContainerId().toString(); - this.logsLink = join( - host, - pathPrefix, - ujoin("logs", nodeId.toString(), amInfo.getContainerId().toString(), - jobId, user)); - this.shortLogsLink = ujoin("logs", nodeId.toString(), amInfo - .getContainerId().toString(), jobId, user); + this.containerId = ""; + this.logsLink = ""; + this.shortLogsLink = ""; + ContainerId containerId = amInfo.getContainerId(); + if (containerId != null) { + this.containerId = containerId.toString(); + this.logsLink = join(host, pathPrefix, + ujoin("logs", this.nodeId, this.containerId, jobId, user)); + this.shortLogsLink = ujoin("logs", this.nodeId, this.containerId, + jobId, user); + } } public String getNodeHttpAddress() { Modified: hadoop/common/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/dao/JobInfo.java URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/dao/JobInfo.java?rev=1227801&r1=1227800&r2=1227801&view=diff ============================================================================== --- hadoop/common/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/dao/JobInfo.java (original) +++ hadoop/common/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/dao/JobInfo.java Thu Jan 5 20:01:20 2012 @@ -92,6 +92,7 @@ public class JobInfo { this.user = job.getUserName(); this.state = job.getState().toString(); this.uberized = job.isUber(); + this.diagnostics = ""; List diagnostics = job.getDiagnostics(); if (diagnostics != null && !diagnostics.isEmpty()) { StringBuffer b = new StringBuffer(); Added: hadoop/common/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/webapp/TestHsWebServices.java URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/webapp/TestHsWebServices.java?rev=1227801&view=auto ============================================================================== --- hadoop/common/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/webapp/TestHsWebServices.java (added) +++ hadoop/common/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/webapp/TestHsWebServices.java Thu Jan 5 20:01:20 2012 @@ -0,0 +1,360 @@ +/** + * 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.mapreduce.v2.hs.webapp; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.io.StringReader; +import java.util.Map; + +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.mapreduce.v2.api.records.JobId; +import org.apache.hadoop.mapreduce.v2.app.AppContext; +import org.apache.hadoop.mapreduce.v2.app.MockJobs; +import org.apache.hadoop.mapreduce.v2.app.job.Job; +import org.apache.hadoop.mapreduce.v2.hs.HistoryContext; +import org.apache.hadoop.mapreduce.v2.hs.JobHistory; +import org.apache.hadoop.util.VersionInfo; +import org.apache.hadoop.yarn.Clock; +import org.apache.hadoop.yarn.api.records.ApplicationAttemptId; +import org.apache.hadoop.yarn.api.records.ApplicationId; +import org.apache.hadoop.yarn.event.EventHandler; +import org.apache.hadoop.yarn.webapp.GenericExceptionHandler; +import org.apache.hadoop.yarn.webapp.WebApp; +import org.apache.hadoop.yarn.webapp.WebServicesTestUtils; +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; + +/** + * Test the History Server info web services api's. Also test non-existent urls. + * + * /ws/v1/history + * /ws/v1/history/info + */ +public class TestHsWebServices extends JerseyTest { + + private static Configuration conf = new Configuration(); + private static TestAppContext appContext; + private static HsWebApp webApp; + + static class TestAppContext implements AppContext { + final ApplicationAttemptId appAttemptID; + final ApplicationId appID; + final String user = MockJobs.newUserName(); + final Map jobs; + final long startTime = System.currentTimeMillis(); + + TestAppContext(int appid, int numJobs, int numTasks, int numAttempts) { + appID = MockJobs.newAppID(appid); + appAttemptID = MockJobs.newAppAttemptID(appID, 0); + jobs = MockJobs.newJobs(appID, numJobs, numTasks, numAttempts); + } + + TestAppContext() { + this(0, 1, 1, 1); + } + + @Override + public ApplicationAttemptId getApplicationAttemptId() { + return appAttemptID; + } + + @Override + public ApplicationId getApplicationID() { + return appID; + } + + @Override + public CharSequence getUser() { + return user; + } + + @Override + public Job getJob(JobId jobID) { + return jobs.get(jobID); + } + + @Override + public Map getAllJobs() { + return jobs; // OK + } + + @SuppressWarnings("rawtypes") + @Override + public EventHandler getEventHandler() { + return null; + } + + @Override + public Clock getClock() { + return null; + } + + @Override + public String getApplicationName() { + return "TestApp"; + } + + @Override + public long getStartTime() { + return startTime; + } + } + + private Injector injector = Guice.createInjector(new ServletModule() { + @Override + protected void configureServlets() { + + appContext = new TestAppContext(); + JobHistory jobHistoryService = new JobHistory(); + HistoryContext historyContext = (HistoryContext) jobHistoryService; + webApp = new HsWebApp(historyContext); + + bind(JAXBContextResolver.class); + bind(HsWebServices.class); + bind(GenericExceptionHandler.class); + bind(WebApp.class).toInstance(webApp); + bind(AppContext.class).toInstance(appContext); + bind(Configuration.class).toInstance(conf); + + 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 TestHsWebServices() { + super(new WebAppDescriptor.Builder( + "org.apache.hadoop.mapreduce.v2.hs.webapp") + .contextListenerClass(GuiceServletConfig.class) + .filterClass(com.google.inject.servlet.GuiceFilter.class) + .contextPath("jersey-guice-filter").servletPath("/").build()); + } + + @Test + public void testHS() throws JSONException, Exception { + WebResource r = resource(); + ClientResponse response = r.path("ws").path("v1").path("history") + .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()); + verifyHSInfo(json.getJSONObject("historyInfo"), appContext); + } + + @Test + public void testHSSlash() throws JSONException, Exception { + WebResource r = resource(); + ClientResponse response = r.path("ws").path("v1").path("history/") + .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()); + verifyHSInfo(json.getJSONObject("historyInfo"), appContext); + } + + @Test + public void testHSDefault() throws JSONException, Exception { + WebResource r = resource(); + ClientResponse response = r.path("ws").path("v1").path("history/") + .get(ClientResponse.class); + assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); + JSONObject json = response.getEntity(JSONObject.class); + assertEquals("incorrect number of elements", 1, json.length()); + verifyHSInfo(json.getJSONObject("historyInfo"), appContext); + } + + @Test + public void testHSXML() throws JSONException, Exception { + WebResource r = resource(); + ClientResponse response = r.path("ws").path("v1").path("history") + .accept(MediaType.APPLICATION_XML).get(ClientResponse.class); + assertEquals(MediaType.APPLICATION_XML_TYPE, response.getType()); + String xml = response.getEntity(String.class); + verifyHSInfoXML(xml, appContext); + } + + @Test + public void testInfo() throws JSONException, Exception { + WebResource r = resource(); + ClientResponse response = r.path("ws").path("v1").path("history") + .path("info").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()); + verifyHSInfo(json.getJSONObject("historyInfo"), appContext); + } + + @Test + public void testInfoSlash() throws JSONException, Exception { + WebResource r = resource(); + ClientResponse response = r.path("ws").path("v1").path("history") + .path("info/").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()); + verifyHSInfo(json.getJSONObject("historyInfo"), appContext); + } + + @Test + public void testInfoDefault() throws JSONException, Exception { + WebResource r = resource(); + ClientResponse response = r.path("ws").path("v1").path("history") + .path("info/").get(ClientResponse.class); + assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); + JSONObject json = response.getEntity(JSONObject.class); + assertEquals("incorrect number of elements", 1, json.length()); + verifyHSInfo(json.getJSONObject("historyInfo"), appContext); + } + + @Test + public void testInfoXML() throws JSONException, Exception { + WebResource r = resource(); + ClientResponse response = r.path("ws").path("v1").path("history") + .path("info/").accept(MediaType.APPLICATION_XML) + .get(ClientResponse.class); + assertEquals(MediaType.APPLICATION_XML_TYPE, response.getType()); + String xml = response.getEntity(String.class); + verifyHSInfoXML(xml, appContext); + } + + @Test + public void testInvalidUri() throws JSONException, Exception { + WebResource r = resource(); + String responseStr = ""; + try { + responseStr = r.path("ws").path("v1").path("history").path("bogus") + .accept(MediaType.APPLICATION_JSON).get(String.class); + fail("should have thrown exception on invalid uri"); + } catch (UniformInterfaceException ue) { + ClientResponse response = ue.getResponse(); + assertEquals(Status.NOT_FOUND, response.getClientResponseStatus()); + WebServicesTestUtils.checkStringMatch( + "error string exists and shouldn't", "", responseStr); + } + } + + @Test + public void testInvalidUri2() throws JSONException, Exception { + WebResource r = resource(); + String responseStr = ""; + try { + responseStr = r.path("ws").path("v1").path("invalid") + .accept(MediaType.APPLICATION_JSON).get(String.class); + fail("should have thrown exception on invalid uri"); + } catch (UniformInterfaceException ue) { + ClientResponse response = ue.getResponse(); + assertEquals(Status.NOT_FOUND, response.getClientResponseStatus()); + WebServicesTestUtils.checkStringMatch( + "error string exists and shouldn't", "", responseStr); + } + } + + @Test + public void testInvalidAccept() throws JSONException, Exception { + WebResource r = resource(); + String responseStr = ""; + try { + responseStr = r.path("ws").path("v1").path("history") + .accept(MediaType.TEXT_PLAIN).get(String.class); + fail("should have thrown exception on invalid uri"); + } catch (UniformInterfaceException ue) { + ClientResponse response = ue.getResponse(); + assertEquals(Status.INTERNAL_SERVER_ERROR, + response.getClientResponseStatus()); + WebServicesTestUtils.checkStringMatch( + "error string exists and shouldn't", "", responseStr); + } + } + + public void verifyHsInfoGeneric(String hadoopVersionBuiltOn, + String hadoopBuildVersion, String hadoopVersion) { + WebServicesTestUtils.checkStringMatch("hadoopVersionBuiltOn", + VersionInfo.getDate(), hadoopVersionBuiltOn); + WebServicesTestUtils.checkStringMatch("hadoopBuildVersion", + VersionInfo.getBuildVersion(), hadoopBuildVersion); + WebServicesTestUtils.checkStringMatch("hadoopVersion", + VersionInfo.getVersion(), hadoopVersion); + } + + public void verifyHSInfo(JSONObject info, TestAppContext ctx) + throws JSONException { + assertEquals("incorrect number of elements", 3, info.length()); + + verifyHsInfoGeneric(info.getString("hadoopVersionBuiltOn"), + info.getString("hadoopBuildVersion"), info.getString("hadoopVersion")); + } + + public void verifyHSInfoXML(String xml, TestAppContext ctx) + throws JSONException, Exception { + 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("historyInfo"); + assertEquals("incorrect number of elements", 1, nodes.getLength()); + + for (int i = 0; i < nodes.getLength(); i++) { + Element element = (Element) nodes.item(i); + verifyHsInfoGeneric( + WebServicesTestUtils.getXmlString(element, "hadoopVersionBuiltOn"), + WebServicesTestUtils.getXmlString(element, "hadoopBuildVersion"), + WebServicesTestUtils.getXmlString(element, "hadoopVersion")); + } + } + +}