/*
* Copyright 2014 the original author or authors.
*
* Licensed 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.springframework.yarn.support.console;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.yarn.api.records.ApplicationReport;
import org.apache.hadoop.yarn.api.records.YarnApplicationState;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Utility class to build reports of applications. Report can either be based
* on states of applications in a resource manager or bundles in hdfs.
* <p>
* Effectively you should be able to use this class to know what applications
* has been installed into hdfs and thus can be deployed, and if deployed what
* are states of those. We also support various types of filters not to be
* too verbose on a console.
*
* @author Janne Valkealahti
*
*/
public class ApplicationsReport {
private final Table table;
/**
* Instantiates a new applications report.
* Private to be only used from a builders.
*
* @param table the rendering table
*/
private ApplicationsReport(Table table) {
this.table = table;
}
@Override
public String toString() {
return table.toString();
}
/**
* Create a new builder for submitted applications.
*
* @return the report builder for submitted applications
*/
public static SubmittedReportBuilder submittedReportBuilder() {
return new SubmittedReportBuilder();
}
/**
* Create a new builder for installed applications.
*
* @return the report builder for installed applications
*/
public static InstalledReportBuilder installedReportBuilder() {
return new InstalledReportBuilder();
}
/**
* Builder for installed applications.
*/
public static class InstalledReportBuilder {
private ArrayList<InstalledField> fields = new ArrayList<InstalledField>();
private FileStatus[] fileStatuses;
private InstalledReportBuilder() {
}
/**
* Adds a new field into a report.
*
* @param f the field
* @return the builder for chaining
*/
public InstalledReportBuilder add(InstalledField f) {
fields.add(f);
return this;
}
/**
* Add a source this report will be build from.
*
* @param fileStatuses the fileStatuses
* @return the builder for chaining
*/
public InstalledReportBuilder from(FileStatus[] fileStatuses) {
this.fileStatuses = fileStatuses;
return this;
}
/**
* Builds an applications report.
*
* @return the applications report
*/
public ApplicationsReport build() {
Table table = new Table();
addHeader(table);
if (!ObjectUtils.isEmpty(fileStatuses)) {
for (FileStatus status : fileStatuses) {
addRow(table, status);
}
}
return new ApplicationsReport(table);
}
private void addHeader(Table table) {
int index = 1;
for (InstalledField f : fields) {
table.addHeader(index++, new TableHeader(f.getName()));
}
}
private void addRow(Table table, FileStatus status) {
final TableRow row = new TableRow();
int index = 1;
for (InstalledField f : fields) {
if (InstalledField.NAME == f) {
row.addValue(index++, status.getPath().getName());
} else if (InstalledField.PATH == f) {
row.addValue(index++, status.getPath().getParent().toString());
}
}
table.getRows().add(row);
}
}
/**
* Builder for submitted applications.
*/
public static class SubmittedReportBuilder {
private ArrayList<SubmittedField> fields = new ArrayList<SubmittedField>();
private SubmittedField sort;
private List<ApplicationReport> reports;
private Map<String, String> headerNameOverrides = new HashMap<String, String>();
private SubmittedReportBuilder() {
}
/**
* Adds a new field into a report.
*
* @param f the field
* @return the builder for chaining
*/
public SubmittedReportBuilder add(SubmittedField f) {
fields.add(f);
return this;
}
/**
* Adds a new fields into a report.
*
* @param f the field
* @return the builder for chaining
*/
public SubmittedReportBuilder add(SubmittedField... f) {
for (SubmittedField ff : f) {
fields.add(ff);
}
return this;
}
/**
* Sort the report by a field.
*
* @param f the f
* @return the builder for chaining
*/
public SubmittedReportBuilder sort(SubmittedField f) {
sort = f;
return this;
}
/**
* Add a source this report will be build from.
*
* @param reports the reports
* @return the builder for chaining
*/
public SubmittedReportBuilder from(List<ApplicationReport> reports) {
this.reports = reports;
return this;
}
public SubmittedReportBuilder header(String from, String to) {
headerNameOverrides.put(from.toLowerCase(), to);
return this;
}
/**
* Builds an applications report.
*
* @return the applications report
*/
public ApplicationsReport build() {
if (sort != null && reports != null) {
Collections.sort(reports, new ApplicationReportComparator(sort));
}
Table table = new Table();
addHeader(table);
if (reports != null) {
for (ApplicationReport report : reports) {
addRow(table, report);
}
}
return new ApplicationsReport(table);
}
private void addHeader(Table table) {
int index = 1;
for (SubmittedField f : fields) {
table.addHeader(index++, new TableHeader(getHeaderNameMayOverride(f)));
}
}
private String getHeaderNameMayOverride(SubmittedField f) {
String n = headerNameOverrides.get(f.toString().toLowerCase());
return StringUtils.hasText(n) ? n : f.getName();
}
private void addRow(Table table, ApplicationReport report) {
final TableRow row = new TableRow();
int index = 1;
for (SubmittedField f : fields) {
if (SubmittedField.ID == f) {
row.addValue(index++, report.getApplicationId().toString());
} else if (SubmittedField.USER == f) {
row.addValue(index++, report.getUser());
} else if (SubmittedField.NAME == f) {
row.addValue(index++, report.getName());
} else if (SubmittedField.QUEUE == f) {
row.addValue(index++, report.getQueue());
} else if (SubmittedField.TYPE == f) {
row.addValue(index++, report.getApplicationType());
} else if (SubmittedField.STARTTIME == f) {
row.addValue(index++, DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(
new Date(report.getStartTime())));
} else if (SubmittedField.FINISHTIME == f) {
long time = report.getFinishTime();
String value = time > 0 ? DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(
new Date(time))
: "N/A";
row.addValue(index++, value);
} else if (SubmittedField.STATE == f) {
row.addValue(index++, report.getYarnApplicationState().toString());
} else if (SubmittedField.FINALSTATUS == f) {
row.addValue(index++, report.getFinalApplicationStatus().toString());
} else if (SubmittedField.ORIGTRACKURL == f) {
String url = report.getYarnApplicationState() == YarnApplicationState.RUNNING ? report.getOriginalTrackingUrl()
: "";
row.addValue(index++, url);
} else if (SubmittedField.TRACKURL == f) {
row.addValue(index++, report.getTrackingUrl());
}
}
table.getRows().add(row);
}
}
/**
* Enums for installed applications fields.
*/
public static enum InstalledField {
NAME,
PATH;
private String name;
private InstalledField() {
}
private InstalledField(String name) {
this.name = name;
}
protected String getName() {
return StringUtils.hasText(name) ? name : this.toString();
}
}
/**
* Enums for submitted applications fields.
*/
public static enum SubmittedField {
ID("APPLICATION ID"),
USER,
NAME,
QUEUE,
TYPE,
STARTTIME,
FINISHTIME,
STATE,
FINALSTATUS,
ORIGTRACKURL("ORIGINAL TRACKING URL"),
TRACKURL("TRACKING URL");
private String name;
private SubmittedField() {
}
private SubmittedField(String name) {
this.name = name;
}
protected String getName() {
return StringUtils.hasText(name) ? name : this.toString();
}
}
/**
* Sort comparator for {@code ApplicationReport}.
*/
private static class ApplicationReportComparator implements Comparator<ApplicationReport> {
private final SubmittedField f;
private ApplicationReportComparator(SubmittedField f) {
this.f = f;
}
@Override
public int compare(ApplicationReport l, ApplicationReport r) {
if (SubmittedField.ID == f) {
return -(l.getApplicationId().toString().compareTo(r.getApplicationId().toString()));
}
else if (SubmittedField.USER == f) {
return l.getUser().compareTo(r.getUser());
}
else {
return 0;
}
}
}
}