diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/AppController.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/AppController.java index 305ec7e29a..e30e1b907d 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/AppController.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/AppController.java @@ -323,6 +323,40 @@ public void conf() { render(confPage()); } + /** + * Handle requests to download the job configuration. + */ + public void downloadConf() { + try { + requireJob(); + } catch (Exception e) { + renderText(e.getMessage()); + return; + } + writeJobConf(); + } + + private void writeJobConf() { + String jobId = $(JOB_ID); + assert(!jobId.isEmpty()); + + JobId jobID = MRApps.toJobID($(JOB_ID)); + Job job = app.context.getJob(jobID); + assert(job != null); + + try { + Configuration jobConf = job.loadConfFile(); + response().setContentType("text/xml"); + response().setHeader("Content-Disposition", + "attachment; filename=" + jobId + ".xml"); + jobConf.writeXml(writer()); + } catch (IOException e) { + LOG.error("Error reading/writing job" + + " conf file for job: " + jobId, e); + renderText(e.getMessage()); + } + } + /** * Render a BAD_REQUEST error. * @param s the error message to include. diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/ConfBlock.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/ConfBlock.java index 4cb79bf37e..532c2bd4fa 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/ConfBlock.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/ConfBlock.java @@ -70,7 +70,7 @@ public class ConfBlock extends HtmlBlock { try { ConfInfo info = new ConfInfo(job); - html.div().h3(confPath.toString())._(); + html.div().a("/jobhistory/downloadconf/" + jid, confPath.toString()); TBODY> tbody = html. // Tasks table table("#conf"). diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/webapp/TestAppController.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/webapp/TestAppController.java index 92786e38e0..3f685b0714 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/webapp/TestAppController.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/webapp/TestAppController.java @@ -24,6 +24,7 @@ import org.apache.commons.lang.StringUtils; import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.Path; import org.apache.hadoop.mapreduce.JobACL; import org.apache.hadoop.mapreduce.v2.api.records.JobId; import org.apache.hadoop.mapreduce.v2.api.records.TaskId; @@ -59,6 +60,8 @@ public void setUp() throws IOException { Task task = mock(Task.class); when(job.getTask(any(TaskId.class))).thenReturn(task); + when(job.loadConfFile()).thenReturn(new Configuration()); + when(job.getConfFile()).thenReturn(new Path("/")); JobId jobID = MRApps.toJobID("job_01_01"); when(context.getJob(jobID)).thenReturn(job); @@ -265,6 +268,17 @@ public void testConfiguration() { assertEquals(JobConfPage.class, appController.getClazz()); } + /** + * Test downloadConf request handling. + */ + @Test + public void testDownloadConfiguration() { + appController.downloadConf(); + String jobConfXml = appController.getData(); + assertTrue("Error downloading the job configuration file.", + !jobConfXml.contains("Error")); + } + /** * Test method 'conf'. Should set AttemptsPage class for rendering or print information about error */ diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsWebApp.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsWebApp.java index ebc6d46c1f..d130910267 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsWebApp.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsWebApp.java @@ -50,6 +50,8 @@ public void setup() { route("/app", HsController.class); route(pajoin("/job", JOB_ID), HsController.class, "job"); route(pajoin("/conf", JOB_ID), HsController.class, "conf"); + routeWithoutDefaultView(pajoin("/downloadconf", JOB_ID), + HsController.class, "downloadConf"); route(pajoin("/jobcounters", JOB_ID), HsController.class, "jobCounters"); route(pajoin("/singlejobcounter",JOB_ID, COUNTER_GROUP, COUNTER_NAME), HsController.class, "singleJobCounter"); diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/Router.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/Router.java index c46b50ea02..f2eca04176 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/Router.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/Router.java @@ -74,17 +74,32 @@ static class Dest { final TreeMap routes = Maps.newTreeMap(); // path->dest + synchronized Dest add(WebApp.HTTP httpMethod, String path, + Class cls, + String action, List names){ + return addWithOptionalDefaultView( + httpMethod, path, cls, action, names, true); + } + + synchronized Dest addWithoutDefaultView(WebApp.HTTP httpMethod, + String path, Class cls, String action, + List names){ + return addWithOptionalDefaultView(httpMethod, path, cls, action, + names, false); + } /** * Add a route to the router. * e.g., add(GET, "/foo/show", FooController.class, "show", [name...]); * The name list is from /foo/show/:name/... */ - synchronized Dest add(WebApp.HTTP httpMethod, String path, - Class cls, - String action, List names) { + synchronized Dest addWithOptionalDefaultView(WebApp.HTTP httpMethod, + String path, Class cls, + String action, List names, boolean defaultViewNeeded) { LOG.debug("adding {}({})->{}#{}", new Object[]{path, names, cls, action}); Dest dest = addController(httpMethod, path, cls, action, names); - addDefaultView(dest); + if (defaultViewNeeded) { + addDefaultView(dest); + } return dest; } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/WebApp.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/WebApp.java index fe800f0852..de6a52bda5 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/WebApp.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/WebApp.java @@ -210,6 +210,19 @@ public void route(HTTP method, String pathSpec, res.subList(R_PARAMS, res.size())); } + /** + * Setup of a webapp serving route without default views added to the page. + * @param pathSpec the path spec in the form of /controller/action/:args etc. + * @param cls the controller class + * @param action the controller method + */ + public void routeWithoutDefaultView(String pathSpec, + Class cls, String action) { + List res = parseRoute(pathSpec); + router.addWithoutDefaultView(HTTP.GET, res.get(R_PATH), cls, action, + res.subList(R_PARAMS, res.size())); + } + public void route(String pathSpec, Class cls, String action) { route(HTTP.GET, pathSpec, cls, action);