diff --git a/hadoop-mapreduce-project/CHANGES.txt b/hadoop-mapreduce-project/CHANGES.txt index bc0c25dda4..24187e6625 100644 --- a/hadoop-mapreduce-project/CHANGES.txt +++ b/hadoop-mapreduce-project/CHANGES.txt @@ -1236,6 +1236,9 @@ Release 0.23.0 - Unreleased MAPREDUCE-2844. Fixed display of nodes in UI. (Ravi Teja Ch N V via acmurthy) + MAPREDUCE-2677. Fixed 404 for some links from HistoryServer. (Robert Evans + via acmurthy) + Release 0.22.0 - Unreleased INCOMPATIBLE CHANGES diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/App.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/App.java index bb28ce68cc..c22ff0538b 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/App.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/App.java @@ -28,11 +28,27 @@ @RequestScoped public class App { final AppContext context; - Job job; - Task task; + private Job job; + private Task task; @Inject App(AppContext ctx) { context = ctx; } + + void setJob(Job job) { + this.job = job; + } + + public Job getJob() { + return job; + } + + void setTask(Task task) { + this.task = task; + } + + public Task getTask() { + return task; + } } 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 adc0e14584..0ca64d1543 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 @@ -31,9 +31,13 @@ import org.apache.hadoop.yarn.util.Apps; import org.apache.hadoop.yarn.util.Times; import org.apache.hadoop.yarn.webapp.Controller; +import org.apache.hadoop.yarn.webapp.View; import com.google.inject.Inject; +/** + * This class renders the various pages that the web app supports. + */ public class AppController extends Controller implements AMParams { final App app; @@ -50,10 +54,16 @@ protected AppController(App app, Configuration conf, RequestContext ctx) { this(app, conf, ctx, "am"); } + /** + * Render the default(index.html) page for the Application Controller + */ @Override public void index() { setTitle(join("MapReduce Application ", $(APP_ID))); } + /** + * Render the /info page with an overview of current application. + */ public void info() { info("Application Master Overview"). _("Application ID:", $(APP_ID)). @@ -65,22 +75,52 @@ public void info() { render(InfoPage.class); } + /** + * @return The class that will render the /job page + */ + protected Class jobPage() { + return JobPage.class; + } + + /** + * Render the /job page + */ public void job() { requireJob(); - render(JobPage.class); + render(jobPage()); } + /** + * @return the class that will render the /jobcounters page + */ + protected Class countersPage() { + return CountersPage.class; + } + + /** + * Render the /jobcounters page + */ public void jobCounters() { requireJob(); - if (app.job != null) { + if (app.getJob() != null) { setTitle(join("Counters for ", $(JOB_ID))); } - render(CountersPage.class); + render(countersPage()); } + /** + * @return the class that will render the /tasks page + */ + protected Class tasksPage() { + return TasksPage.class; + } + + /** + * Render the /tasks page + */ public void tasks() { requireJob(); - if (app.job != null) { + if (app.getJob() != null) { try { String tt = $(TASK_TYPE); tt = tt.isEmpty() ? "All" : StringUtils.capitalize(MRApps.taskType(tt). @@ -90,20 +130,40 @@ public void tasks() { badRequest(e.getMessage()); } } - render(TasksPage.class); + render(tasksPage()); } + /** + * @return the class that will render the /task page + */ + protected Class taskPage() { + return TaskPage.class; + } + + /** + * Render the /task page + */ public void task() { requireTask(); - if (app.task != null) { + if (app.getTask() != null) { setTitle(join("Attempts for ", $(TASK_ID))); } - render(TaskPage.class); + render(taskPage()); } + /** + * @return the class that will render the /attempts page + */ + protected Class attemptsPage() { + return AttemptsPage.class; + } + + /** + * Render the attempts page + */ public void attempts() { requireJob(); - if (app.job != null) { + if (app.getJob() != null) { try { String taskType = $(TASK_TYPE); if (taskType.isEmpty()) { @@ -119,27 +179,38 @@ public void attempts() { badRequest(e.getMessage()); } } - render(AttemptsPage.class); + render(attemptsPage()); } + /** + * Render a BAD_REQUEST error. + * @param s the error message to include. + */ void badRequest(String s) { setStatus(response().SC_BAD_REQUEST); setTitle(join("Bad request: ", s)); } + /** + * Render a NOT_FOUND error. + * @param s the error message to include. + */ void notFound(String s) { setStatus(response().SC_NOT_FOUND); setTitle(join("Not found: ", s)); } + /** + * Ensure that a JOB_ID was passed into the page. + */ void requireJob() { try { if ($(JOB_ID).isEmpty()) { throw new RuntimeException("missing job ID"); } JobId jobID = MRApps.toJobID($(JOB_ID)); - app.job = app.context.getJob(jobID); - if (app.job == null) { + app.setJob(app.context.getJob(jobID)); + if (app.getJob() == null) { notFound($(JOB_ID)); } } catch (Exception e) { @@ -147,18 +218,21 @@ void requireJob() { } } + /** + * Ensure that a TASK_ID was passed into the page. + */ void requireTask() { try { if ($(TASK_ID).isEmpty()) { throw new RuntimeException("missing task ID"); } TaskId taskID = MRApps.toTaskID($(TASK_ID)); - app.job = app.context.getJob(taskID.getJobId()); - if (app.job == null) { + app.setJob(app.context.getJob(taskID.getJobId())); + if (app.getJob() == null) { notFound(MRApps.toString(taskID.getJobId())); } else { - app.task = app.job.getTask(taskID); - if (app.task == null) { + app.setTask(app.getJob().getTask(taskID)); + if (app.getTask() == null) { notFound($(TASK_ID)); } } diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/AttemptsPage.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/AttemptsPage.java index d0cd23e686..92ae7b6f84 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/AttemptsPage.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/AttemptsPage.java @@ -57,7 +57,7 @@ protected Collection getTaskAttempts() { String attemptStateStr = $(ATTEMPT_STATE); TaskAttemptStateUI neededState = MRApps .taskAttemptState(attemptStateStr); - for (Task task : super.app.job.getTasks(taskType).values()) { + for (Task task : super.app.getJob().getTasks(taskType).values()) { Map attempts = task.getAttempts(); for (TaskAttempt attempt : attempts.values()) { if (neededState.correspondsTo(attempt.getState())) { diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/NavBlock.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/NavBlock.java index c7d6751300..5bbfc24f2b 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/NavBlock.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/NavBlock.java @@ -45,8 +45,8 @@ public class NavBlock extends HtmlBlock { ul(). li().a(url("app/info"), "About")._(). li().a(url("app"), "Jobs")._()._(); - if (app.job != null) { - String jobid = MRApps.toString(app.job.getID()); + if (app.getJob() != null) { + String jobid = MRApps.toString(app.getJob().getID()); nav. h3("Job"). ul(). diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/TaskPage.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/TaskPage.java index 3671df01e4..d9884d146a 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/TaskPage.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/TaskPage.java @@ -98,11 +98,11 @@ protected void render(Block html) { } protected boolean isValidRequest() { - return app.task != null; + return app.getTask() != null; } protected Collection getTaskAttempts() { - return app.task.getAttempts().values(); + return app.getTask().getAttempts().values(); } } diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/TasksBlock.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/TasksBlock.java index c44453bfe9..7247761d65 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/TasksBlock.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/webapp/TasksBlock.java @@ -42,7 +42,7 @@ public class TasksBlock extends HtmlBlock { } @Override protected void render(Block html) { - if (app.job == null) { + if (app.getJob() == null) { html. h2($(TITLE)); return; @@ -63,7 +63,7 @@ public class TasksBlock extends HtmlBlock { th("Finish Time"). th("Elapsed Time")._()._(). tbody(); - for (Task task : app.job.getTasks().values()) { + for (Task task : app.getJob().getTasks().values()) { if (type != null && task.getType() != type) { continue; } diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/pom.xml b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/pom.xml index f678d6be76..b9e409cad6 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/pom.xml +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/pom.xml @@ -43,6 +43,12 @@ test-jar test + + org.apache.hadoop + hadoop-yarn-common + test-jar + test + diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/CompletedJob.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/CompletedJob.java index d29139f2df..3af30088d8 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/CompletedJob.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/CompletedJob.java @@ -279,7 +279,8 @@ public int getTotalReduces() { @Override public boolean isUber() { - throw new YarnException("Not yet implemented!"); + LOG.warn("isUber is not yet implemented"); + return false; } @Override diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/HistoryClientService.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/HistoryClientService.java index a848edcd6e..d0893648e0 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/HistoryClientService.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/HistoryClientService.java @@ -62,7 +62,7 @@ import org.apache.hadoop.mapreduce.v2.api.records.TaskType; import org.apache.hadoop.mapreduce.v2.app.job.Job; import org.apache.hadoop.mapreduce.v2.app.job.Task; -import org.apache.hadoop.mapreduce.v2.hs.webapp.HSWebApp; +import org.apache.hadoop.mapreduce.v2.hs.webapp.HsWebApp; import org.apache.hadoop.mapreduce.v2.jobhistory.JHConfig; import org.apache.hadoop.mapreduce.v2.security.client.ClientHSSecurityInfo; import org.apache.hadoop.net.NetUtils; @@ -132,7 +132,7 @@ public void start() { } private void initializeWebApp(Configuration conf) { - webApp = new HSWebApp(history); + webApp = new HsWebApp(history); String bindAddress = conf.get(JHConfig.HS_WEBAPP_BIND_ADDRESS, JHConfig.DEFAULT_HS_WEBAPP_BIND_ADDRESS); WebApps.$for("yarn", this).at(bindAddress).start(webApp); diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsAboutPage.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsAboutPage.java new file mode 100644 index 0000000000..4c9f667b5b --- /dev/null +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsAboutPage.java @@ -0,0 +1,52 @@ +/** +* 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.apache.hadoop.yarn.webapp.view.JQueryUI.ACCORDION; +import static org.apache.hadoop.yarn.webapp.view.JQueryUI.initID; + +import org.apache.hadoop.util.VersionInfo; +import org.apache.hadoop.yarn.webapp.SubView; +import org.apache.hadoop.yarn.webapp.view.InfoBlock; + +/** + * A Page the shows info about the history server + */ +public class HsAboutPage extends HsView { + + /* + * (non-Javadoc) + * @see org.apache.hadoop.mapreduce.v2.hs.webapp.HsView#preHead(org.apache.hadoop.yarn.webapp.hamlet.Hamlet.HTML) + */ + @Override protected void preHead(Page.HTML<_> html) { + commonPreHead(html); + //override the nav config from commonPReHead + set(initID(ACCORDION, "nav"), "{autoHeight:false, active:0}"); + } + + /** + * The content of this page is the attempts block + * @return AttemptsBlock.class + */ + @Override protected Class content() { + info("History Server"). + _("BuildVersion", VersionInfo.getBuildVersion()); + return InfoBlock.class; + } +} diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsController.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsController.java index fab300853b..a0e36cf90b 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsController.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsController.java @@ -21,41 +21,123 @@ import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.mapreduce.v2.app.webapp.App; import org.apache.hadoop.mapreduce.v2.app.webapp.AppController; +import org.apache.hadoop.yarn.webapp.View; import com.google.inject.Inject; +/** + * This class renders the various pages that the History Server WebApp supports + */ public class HsController extends AppController { @Inject HsController(App app, Configuration conf, RequestContext ctx) { super(app, conf, ctx, "History"); } + /* + * (non-Javadoc) + * @see org.apache.hadoop.mapreduce.v2.app.webapp.AppController#index() + */ @Override public void index() { - // TODO Auto-generated method stub setTitle("JobHistory"); } + + /* + * (non-Javadoc) + * @see org.apache.hadoop.mapreduce.v2.app.webapp.AppController#jobPage() + */ + @Override + protected Class jobPage() { + return HsJobPage.class; + } + + /* + * (non-Javadoc) + * @see org.apache.hadoop.mapreduce.v2.app.webapp.AppController#countersPage() + */ + @Override + protected Class countersPage() { + return HsCountersPage.class; + } + + /* + * (non-Javadoc) + * @see org.apache.hadoop.mapreduce.v2.app.webapp.AppController#tasksPage() + */ + @Override + protected Class tasksPage() { + return HsTasksPage.class; + } + + /* + * (non-Javadoc) + * @see org.apache.hadoop.mapreduce.v2.app.webapp.AppController#taskPage() + */ + @Override + protected Class taskPage() { + return HsTaskPage.class; + } // Need all of these methods here also as Guice doesn't look into parent // classes. + + /* + * (non-Javadoc) + * @see org.apache.hadoop.mapreduce.v2.app.webapp.AppController#job() + */ + @Override public void job() { super.job(); } + /* + * (non-Javadoc) + * @see org.apache.hadoop.mapreduce.v2.app.webapp.AppController#jobCounters() + */ + @Override public void jobCounters() { super.jobCounters(); } + /* + * (non-Javadoc) + * @see org.apache.hadoop.mapreduce.v2.app.webapp.AppController#tasks() + */ + @Override public void tasks() { super.tasks(); } + /* + * (non-Javadoc) + * @see org.apache.hadoop.mapreduce.v2.app.webapp.AppController#task() + */ + @Override public void task() { super.task(); } + /* + * (non-Javadoc) + * @see org.apache.hadoop.mapreduce.v2.app.webapp.AppController#attempts() + */ @Override public void attempts() { super.attempts(); } + + /** + * @return the page about the current server. + */ + protected Class aboutPage() { + return HsAboutPage.class; + } + + /** + * Render a page about the current server. + */ + public void about() { + render(aboutPage()); + } } diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsCountersPage.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsCountersPage.java new file mode 100644 index 0000000000..0840f91357 --- /dev/null +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsCountersPage.java @@ -0,0 +1,63 @@ +/** +* 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 org.apache.hadoop.mapreduce.v2.app.webapp.CountersBlock; +import org.apache.hadoop.yarn.webapp.SubView; + +import static org.apache.hadoop.yarn.webapp.view.JQueryUI.*; + +/** + * Render the counters page + */ +public class HsCountersPage extends HsView { + + /* + * (non-Javadoc) + * @see org.apache.hadoop.mapreduce.v2.hs.webapp.HsView#preHead(org.apache.hadoop.yarn.webapp.hamlet.Hamlet.HTML) + */ + @Override protected void preHead(Page.HTML<_> html) { + commonPreHead(html); + set(initID(ACCORDION, "nav"), "{autoHeight:false, active:1}"); + set(DATATABLES_SELECTOR, "#counters .dt-counters"); + set(initSelector(DATATABLES), + "{bJQueryUI:true, sDom:'t', iDisplayLength:-1}"); + } + + /* + * (non-Javadoc) + * @see org.apache.hadoop.yarn.webapp.view.TwoColumnLayout#postHead(org.apache.hadoop.yarn.webapp.hamlet.Hamlet.HTML) + */ + @Override protected void postHead(Page.HTML<_> html) { + html. + style("#counters, .dt-counters { table-layout: fixed }", + "#counters th { overflow: hidden; vertical-align: center }", + "#counters .dataTables_wrapper { min-height: 1em }", + "#counters .group { width: 10em }", + "#counters .name { width: 30em }"); + } + + /** + * The content of this page is the CountersBlock now. + * @return CountersBlock.class + */ + @Override protected Class content() { + return CountersBlock.class; + } +} diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsJobPage.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsJobPage.java new file mode 100644 index 0000000000..5def9cde51 --- /dev/null +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsJobPage.java @@ -0,0 +1,54 @@ +/** +* 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.apache.hadoop.mapreduce.v2.app.webapp.AMParams.JOB_ID; +import static org.apache.hadoop.yarn.util.StringHelper.join; +import static org.apache.hadoop.yarn.webapp.view.JQueryUI.ACCORDION; +import static org.apache.hadoop.yarn.webapp.view.JQueryUI.initID; + +import org.apache.hadoop.mapreduce.v2.app.webapp.JobBlock; +import org.apache.hadoop.yarn.webapp.SubView; + +/** + * Render a page that describes a specific job. + */ +public class HsJobPage extends HsView { + + /* + * (non-Javadoc) + * @see org.apache.hadoop.mapreduce.v2.hs.webapp.HsView#preHead(org.apache.hadoop.yarn.webapp.hamlet.Hamlet.HTML) + */ + @Override protected void preHead(Page.HTML<_> html) { + String jobID = $(JOB_ID); + set(TITLE, jobID.isEmpty() ? "Bad request: missing job ID" + : join("MapReduce Job ", $(JOB_ID))); + commonPreHead(html); + //Override the nav config from the commonPreHead + set(initID(ACCORDION, "nav"), "{autoHeight:false, active:1}"); + } + + /** + * The content of this page is the JobBlock + * @return JobBlock.class + */ + @Override protected Class content() { + return JobBlock.class; + } +} diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsNavBlock.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsNavBlock.java new file mode 100644 index 0000000000..b159daefa1 --- /dev/null +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsNavBlock.java @@ -0,0 +1,66 @@ +/** +* 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 org.apache.hadoop.mapreduce.v2.app.webapp.App; +import org.apache.hadoop.mapreduce.v2.util.MRApps; +import org.apache.hadoop.yarn.webapp.hamlet.Hamlet; +import org.apache.hadoop.yarn.webapp.hamlet.Hamlet.DIV; +import org.apache.hadoop.yarn.webapp.view.HtmlBlock; + +import com.google.inject.Inject; + +/** + * The navigation block for the history server + */ +public class HsNavBlock extends HtmlBlock { + final App app; + + @Inject HsNavBlock(App app) { this.app = app; } + + /* + * (non-Javadoc) + * @see org.apache.hadoop.yarn.webapp.view.HtmlBlock#render(org.apache.hadoop.yarn.webapp.view.HtmlBlock.Block) + */ + @Override protected void render(Block html) { + DIV nav = html. + div("#nav"). + h3("Application"). + ul(). + li().a("about", "About")._(). + li().a(url("app"), "Jobs")._()._(); + if (app.getJob() != null) { + String jobid = MRApps.toString(app.getJob().getID()); + nav. + h3("Job"). + ul(). + li().a(url("job", jobid), "Overview")._(). + li().a(url("jobcounters", jobid), "Counters")._(). + li().a(url("tasks", jobid, "m"), "Map tasks")._(). + li().a(url("tasks", jobid, "r"), "Reduce tasks")._()._(); + } + nav. + h3("Tools"). + ul(). + li().a("/conf", "Configuration")._(). + li().a("/stacks", "Server stacks")._(). + li().a("/metrics", "Server metrics")._()._()._(). + div("#themeswitcher")._(); + } +} diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsTaskPage.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsTaskPage.java new file mode 100644 index 0000000000..d9202b3986 --- /dev/null +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsTaskPage.java @@ -0,0 +1,159 @@ +/** +* 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.apache.hadoop.yarn.util.StringHelper.percent; +import static org.apache.hadoop.yarn.webapp.view.JQueryUI.ACCORDION; +import static org.apache.hadoop.yarn.webapp.view.JQueryUI.DATATABLES; +import static org.apache.hadoop.yarn.webapp.view.JQueryUI.DATATABLES_ID; +import static org.apache.hadoop.yarn.webapp.view.JQueryUI.initID; +import static org.apache.hadoop.yarn.webapp.view.JQueryUI.tableInit; + +import java.util.Collection; + +import org.apache.hadoop.mapreduce.v2.app.job.TaskAttempt; +import org.apache.hadoop.mapreduce.v2.app.webapp.App; +import org.apache.hadoop.mapreduce.v2.util.MRApps; +import org.apache.hadoop.util.StringUtils; +import org.apache.hadoop.yarn.api.records.ContainerId; +import org.apache.hadoop.yarn.util.ConverterUtils; +import org.apache.hadoop.yarn.util.Times; +import org.apache.hadoop.yarn.webapp.SubView; +import org.apache.hadoop.yarn.webapp.hamlet.Hamlet; +import org.apache.hadoop.yarn.webapp.hamlet.Hamlet.TABLE; +import org.apache.hadoop.yarn.webapp.hamlet.Hamlet.TBODY; +import org.apache.hadoop.yarn.webapp.hamlet.Hamlet.TD; +import org.apache.hadoop.yarn.webapp.hamlet.Hamlet.TR; +import org.apache.hadoop.yarn.webapp.view.HtmlBlock; + +import com.google.common.base.Joiner; +import com.google.inject.Inject; + +/** + * A Page the shows the status of a given task + */ +public class HsTaskPage extends HsView { + + /** + * A Block of HTML that will render a given task attempt. + */ + static class AttemptsBlock extends HtmlBlock { + final App app; + + @Inject + AttemptsBlock(App ctx) { + app = ctx; + } + + @Override + protected void render(Block html) { + if (!isValidRequest()) { + html. + h2($(TITLE)); + return; + } + TBODY> tbody = html. + table("#attempts"). + thead(). + tr(). + th(".id", "Attempt"). + th(".progress", "Progress"). + th(".state", "State"). + th(".node", "node"). + th(".tsh", "Started"). + th(".tsh", "Finished"). + th(".tsh", "Elapsed"). + th(".note", "Note")._()._(). + tbody(); + for (TaskAttempt ta : getTaskAttempts()) { + String taid = MRApps.toString(ta.getID()); + String progress = percent(ta.getProgress()); + ContainerId containerId = ta.getAssignedContainerID(); + + String nodeHttpAddr = ta.getNodeHttpAddress(); + long startTime = ta.getLaunchTime(); + long finishTime = ta.getFinishTime(); + long elapsed = Times.elapsed(startTime, finishTime); + TD>>> nodeTd = tbody. + tr(). + td(".id", taid). + td(".progress", progress). + td(".state", ta.getState().toString()). + td(). + a(".nodelink", url("http://", nodeHttpAddr), nodeHttpAddr); + if (containerId != null) { + String containerIdStr = ConverterUtils.toString(containerId); + nodeTd._(" "). + a(".logslink", url("http://", nodeHttpAddr, "yarn", "containerlogs", + containerIdStr), "logs"); + } + nodeTd._(). + td(".ts", Times.format(startTime)). + td(".ts", Times.format(finishTime)). + td(".dt", StringUtils.formatTime(elapsed)). + td(".note", Joiner.on('\n').join(ta.getDiagnostics()))._(); + } + tbody._()._(); + } + + /** + * @return true if this is a valid request else false. + */ + protected boolean isValidRequest() { + return app.getTask() != null; + } + + /** + * @return all of the attempts to render. + */ + protected Collection getTaskAttempts() { + return app.getTask().getAttempts().values(); + } + } + + /* + * (non-Javadoc) + * @see org.apache.hadoop.mapreduce.v2.hs.webapp.HsView#preHead(org.apache.hadoop.yarn.webapp.hamlet.Hamlet.HTML) + */ + @Override protected void preHead(Page.HTML<_> html) { + commonPreHead(html); + //override the nav config from commonPReHead + set(initID(ACCORDION, "nav"), "{autoHeight:false, active:1}"); + //Set up the java script and CSS for the attempts table + set(DATATABLES_ID, "attempts"); + set(initID(DATATABLES, "attempts"), attemptsTableInit()); + setTableStyles(html, "attempts"); + } + + /** + * The content of this page is the attempts block + * @return AttemptsBlock.class + */ + @Override protected Class content() { + return AttemptsBlock.class; + } + + /** + * @return The end of the JS map that is the jquery datatable config for the + * attempts table. + */ + private String attemptsTableInit() { + return tableInit().append("}").toString(); + } +} diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsTasksPage.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsTasksPage.java new file mode 100644 index 0000000000..c3ddf05b30 --- /dev/null +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsTasksPage.java @@ -0,0 +1,65 @@ +/** +* 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.apache.hadoop.yarn.webapp.view.JQueryUI.ACCORDION; +import static org.apache.hadoop.yarn.webapp.view.JQueryUI.DATATABLES; +import static org.apache.hadoop.yarn.webapp.view.JQueryUI.DATATABLES_ID; +import static org.apache.hadoop.yarn.webapp.view.JQueryUI.initID; +import static org.apache.hadoop.yarn.webapp.view.JQueryUI.tableInit; + +import org.apache.hadoop.mapreduce.v2.app.webapp.TasksBlock; +import org.apache.hadoop.yarn.webapp.SubView; + +/** + * A page showing the tasks for a given application. + */ +public class HsTasksPage extends HsView { + + /* + * (non-Javadoc) + * @see org.apache.hadoop.mapreduce.v2.hs.webapp.HsView#preHead(org.apache.hadoop.yarn.webapp.hamlet.Hamlet.HTML) + */ + @Override protected void preHead(Page.HTML<_> html) { + commonPreHead(html); + set(DATATABLES_ID, "tasks"); + set(initID(ACCORDION, "nav"), "{autoHeight:false, active:1}"); + set(initID(DATATABLES, "tasks"), tasksTableInit()); + setTableStyles(html, "tasks"); + } + + /** + * The content of this page is the TasksBlock + * @return TasksBlock.class + */ + @Override protected Class content() { + return TasksBlock.class; + } + + /** + * @return the end of the JS map that is the jquery datatable configuration + * for the tasks table. + */ + private String tasksTableInit() { + return tableInit(). + append(",aoColumns:[{sType:'title-numeric'},{sType:'title-numeric',"). + append("bSearchable:false},null,{sType:'title-numeric'},"). + append("{sType:'title-numeric'},{sType:'title-numeric'}]}").toString(); + } +} diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsView.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsView.java index 194571f209..4374c9355a 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsView.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsView.java @@ -24,7 +24,14 @@ import static org.apache.hadoop.yarn.webapp.view.JQueryUI.*; +/** + * A view that should be used as the base class for all history server pages. + */ public class HsView extends TwoColumnLayout { + /* + * (non-Javadoc) + * @see org.apache.hadoop.yarn.webapp.view.TwoColumnLayout#preHead(org.apache.hadoop.yarn.webapp.hamlet.Hamlet.HTML) + */ @Override protected void preHead(Page.HTML<_> html) { commonPreHead(html); set(DATATABLES_ID, "jobs"); @@ -32,10 +39,13 @@ public class HsView extends TwoColumnLayout { setTableStyles(html, "jobs"); } + /** + * The prehead that should be common to all subclasses. + * @param html used to render. + */ protected void commonPreHead(Page.HTML<_> html) { - //html.meta_http("refresh", "10"); set(ACCORDION_ID, "nav"); - set(initID(ACCORDION, "nav"), "{autoHeight:false, active:1}"); + set(initID(ACCORDION, "nav"), "{autoHeight:false, active:0}"); set(THEMESWITCHER_ID, "themeswitcher"); } @@ -43,17 +53,27 @@ protected void commonPreHead(Page.HTML<_> html) { * (non-Javadoc) * @see org.apache.hadoop.yarn.webapp.view.TwoColumnLayout#nav() */ - @Override protected Class nav() { - return org.apache.hadoop.mapreduce.v2.app.webapp.NavBlock.class; + return HsNavBlock.class; } + /* + * (non-Javadoc) + * @see org.apache.hadoop.yarn.webapp.view.TwoColumnLayout#content() + */ @Override protected Class content() { return JobsBlock.class; } - + + //TODO We need a way to move all of the javascript/CSS that is for a subview + // into that subview. + /** + * @return The end of a javascript map that is the jquery datatable + * configuration for the jobs table. the Jobs table is assumed to be + * rendered by the class returned from {@link #content()} + */ private String jobsTableInit() { return tableInit(). append(",aoColumns:[{sType:'title-numeric'},"). 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 similarity index 91% rename from hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HSWebApp.java rename to hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/main/java/org/apache/hadoop/mapreduce/v2/hs/webapp/HsWebApp.java index 83f5c4e36a..5273a70d22 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 @@ -25,11 +25,11 @@ import org.apache.hadoop.mapreduce.v2.hs.HistoryContext; import org.apache.hadoop.yarn.webapp.WebApp; -public class HSWebApp extends WebApp implements AMParams { +public class HsWebApp extends WebApp implements AMParams { private HistoryContext history; - public HSWebApp(HistoryContext history) { + public HsWebApp(HistoryContext history) { this.history = history; } @@ -44,6 +44,7 @@ public void setup() { route(pajoin("/attempts", JOB_ID, TASK_TYPE, ATTEMPT_STATE), HsController.class, "attempts"); route(pajoin("/task", TASK_ID), HsController.class, "task"); + route("/about", HsController.class, "about"); } } diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/webapp/TestHSWebApp.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/webapp/TestHSWebApp.java index e96f198748..736987a468 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/webapp/TestHSWebApp.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/webapp/TestHSWebApp.java @@ -1,27 +1,126 @@ /** - * 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. - */ +* 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 org.apache.hadoop.yarn.webapp.WebApps; +import static org.apache.hadoop.mapreduce.v2.app.webapp.AMParams.APP_ID; +import static org.junit.Assert.assertEquals; + +import java.util.Map; + +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.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.util.Apps; +import org.apache.hadoop.yarn.webapp.test.WebAppTests; +import org.junit.Test; + +import com.google.inject.Injector; public class TestHSWebApp { - public static void main(String[] args) { - WebApps.$for("yarn").at(19888).start().joinThread(); + + 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 + } + + @Override + public EventHandler getEventHandler() { + return null; + } + + @Override + public Clock getClock() { + return null; + } + + @Override + public String getApplicationName() { + return "TestApp"; + } + + @Override + public long getStartTime() { + return startTime; + } + } + + @Test public void testAppControllerIndex() { + TestAppContext ctx = new TestAppContext(); + Injector injector = WebAppTests.createMockInjector(AppContext.class, ctx); + HsController controller = injector.getInstance(HsController.class); + controller.index(); + assertEquals(Apps.toString(ctx.appID), controller.get(APP_ID,"")); + } + + @Test public void testJobView() { + WebAppTests.testPage(HsJobPage.class, AppContext.class, new TestAppContext()); + } + + @Test public void testTasksView() { + WebAppTests.testPage(HsTasksPage.class, AppContext.class, + new TestAppContext()); + } + + @Test public void testTaskView() { + WebAppTests.testPage(HsTaskPage.class, AppContext.class, + new TestAppContext()); } } diff --git a/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/view/HtmlPage.java b/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/view/HtmlPage.java index c33fa97a73..4ff504ce93 100644 --- a/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/view/HtmlPage.java +++ b/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/view/HtmlPage.java @@ -26,6 +26,11 @@ import org.apache.hadoop.yarn.webapp.WebAppException; import org.apache.hadoop.yarn.webapp.hamlet.Hamlet; +/** + * The parent class of all HTML pages. Override + * {@link #render(org.apache.hadoop.yarn.webapp.hamlet.Hamlet.HTML)} + * to actually render the page. + */ public abstract class HtmlPage extends TextView { public static class _ implements Hamlet._ { @@ -79,6 +84,10 @@ public void render() { } } + /** + * Render the the HTML page. + * @param html the page to render data to. + */ protected abstract void render(Page.HTML<_> html); } diff --git a/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/view/TwoColumnLayout.java b/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/view/TwoColumnLayout.java index c79e7de0c2..f32ab38f05 100644 --- a/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/view/TwoColumnLayout.java +++ b/hadoop-mapreduce-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/webapp/view/TwoColumnLayout.java @@ -18,21 +18,25 @@ package org.apache.hadoop.yarn.webapp.view; -import com.google.common.collect.Lists; -import com.google.inject.Inject; -import java.util.List; +import static org.apache.hadoop.yarn.util.StringHelper.join; -import static org.apache.hadoop.yarn.util.StringHelper.*; -import static org.apache.hadoop.yarn.webapp.Params.*; +import java.util.List; import org.apache.hadoop.yarn.webapp.SubView; +import com.google.common.collect.Lists; + /** - * A simpler two column layout implementation. Works with resizable themes. + * A simpler two column layout implementation with a header, a navigation bar + * on the left, content on the right, and a footer. Works with resizable themes. * @see TwoColumnCssLayout */ public class TwoColumnLayout extends HtmlPage { + /* + * (non-Javadoc) + * @see org.apache.hadoop.yarn.webapp.view.HtmlPage#render(org.apache.hadoop.yarn.webapp.hamlet.Hamlet.HTML) + */ @Override protected void render(Page.HTML<_> html) { preHead(html); html. @@ -65,28 +69,55 @@ public class TwoColumnLayout extends HtmlPage { _(content())._()._()._()._()._(); } + /** + * Do what needs to be done before the header is rendered. This usually + * involves setting page variables for Javascript and CSS rendering. + * @param html the html to use to render. + */ protected void preHead(Page.HTML<_> html) { } + /** + * Do what needs to be done after the header is rendered. + * @param html the html to use to render. + */ protected void postHead(Page.HTML<_> html) { } + /** + * @return the class that will render the header of the page. + */ protected Class header() { return HeaderBlock.class; } + /** + * @return the class that will render the content of the page. + */ protected Class content() { return LipsumBlock.class; } + /** + * @return the class that will render the navigation bar. + */ protected Class nav() { return NavBlock.class; } + /** + * @return the class that will render the footer. + */ protected Class footer() { return FooterBlock.class; } + /** + * Sets up a table to be a consistent style. + * @param html the HTML to use to render. + * @param tableId the ID of the table to set styles on. + * @param innerStyles any other styles to add to the table. + */ protected void setTableStyles(Page.HTML<_> html, String tableId, String... innerStyles) { List styles = Lists.newArrayList();