class JobExceptionsHandler extends AbstractExecutionGraphRequestHandler {

	public static String createJobExceptionsJson(AccessExecutionGraph graph) throws IOException {
		StringWriter writer = new StringWriter();
		JsonGenerator gen = JsonFactory.JACKSON_FACTORY.createGenerator(writer);

		gen.writeStartObject();

		// most important is the root failure cause
		ErrorInfo rootException = graph.getFailureInfo();
		if (rootException != null) {
			gen.writeStringField("root-exception", rootException.getExceptionAsString());
			gen.writeNumberField("timestamp", rootException.getTimestamp());
		}

		// we additionally collect all exceptions (up to a limit) that occurred in the individual tasks
		gen.writeArrayFieldStart("all-exceptions");

		int numExceptionsSoFar = 0;
		boolean truncated = false;

		for (AccessExecutionVertex task : graph.getAllExecutionVertices()) {
			String t = task.getFailureCauseAsString();
			if (t != null && !t.equals(ExceptionUtils.STRINGIFIED_NULL_EXCEPTION)) {
				if (numExceptionsSoFar >= MAX_NUMBER_EXCEPTION_TO_REPORT) {
					truncated = true;
					break;
				}

				TaskManagerLocation location = task.getCurrentAssignedResourceLocation();
				String locationString = location != null ?
						location.getFQDNHostname() + ':' + location.dataPort() : "(unassigned)";

				gen.writeStartObject();
				gen.writeStringField("exception", t);
				gen.writeStringField("task", task.getTaskNameWithSubtaskIndex());
				gen.writeStringField("location", locationString);
				long timestamp = task.getStateTimestamp(ExecutionState.FAILED);
				gen.writeNumberField("timestamp", timestamp == 0 ? -1 : timestamp);
				gen.writeEndObject();
				numExceptionsSoFar++;
			}
		}
		gen.writeEndArray();

		gen.writeBooleanField("truncated", truncated);
		gen.writeEndObject();

		gen.close();
		return writer.toString();
	}

	public  JobExceptionsHandler(ExecutionGraphCache executionGraphHolder, Executor executor);

}