package dk.statsbiblioteket.medieplatform.autonomous;
import dk.statsbiblioteket.util.Strings;
import org.slf4j.Logger;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
/** This class collects the result of a run of a component. */
public class ResultCollector {
private static Logger log = org.slf4j.LoggerFactory.getLogger(ResultCollector.class);
private Result resultStructure;
private boolean preservable = true;
private Integer maxResults;
private int resultCount;
private Failure lastFailure;
/**
*
* @param tool
* @param version
* @param maxResults The maximum number of results to collect. If null then unlimited results may be collected.
*/
public ResultCollector(String tool, String version, Integer maxResults) {
resultStructure = new ObjectFactory().createResult();
setSuccess(true);
resultStructure.setFailures(new Failures());
resultStructure.setTool(tool);
resultStructure.setVersion(version);
setTimestamp(new Date());
this.maxResults = maxResults;
resultCount = 0;
}
/**
* This method is marked as deprecated. Use the alternative constructor where the maximum number of allowable
* results is explicitly specified.
* @param tool
* @param version
*/
@Deprecated
public ResultCollector(String tool, String version) {
this(tool, version, null);
}
/**
* This flag controls whether or not the result collecter contains results that should be preserved. Default
* true. If set to false, the result will not be preserved and thus the event will never have happened in
* the event framework
*/
public boolean isPreservable() {
return preservable;
}
/**
* This flag controls whether or not the result collecter contains results that should be preserved. Default
* true. If set to false, the result will not be preserved and thus the event will never have happened in
* the event framework
*/
public void setPreservable(boolean preservable) {
this.preservable = preservable;
}
/**
* Get the success value of the execution
*
* @return the success
*/
public boolean isSuccess() {
return "Success".equals(resultStructure.getOutcome());
}
/**
* Set the success Value of the execution
*
* @param success the sucesss
*/
private void setSuccess(boolean success) {
resultStructure.setOutcome(success ? "Success" : "Failure");
}
/**
* Add a specific failure to the result collector. All these parameters must be non-null and non-empty
*
* @param reference the reference to the file/object that caused the failure
* @param type the type of failure
* @param component the component that failed
* @param description Description of the failure.
*/
public void addFailure(String reference, String type, String component, String description) {
addFailure(reference, type, component, description, new String[]{});
}
/**
* Add a specific failure to the result collector. All these parameters, except the last, must be non-null and non-empty
*
* @param reference the reference to the file/object that caused the failure
* @param type the type of failure
* @param component the component that failed
* @param description Description of the failure.
* @param details additional details, can be null
*/
public synchronized void addFailure(String reference, String type, String component, String description, String... details) {
resultCount++; //The count of the current failure, starting at 1.
log.info(
"Adding failure for " +
"resource '{}' " +
"of type '{}' " +
"from component '{}' " +
"with description '{}' " +
"and details '{}'", reference, type, component, description, Strings.join(details, "\n"));
List<Failure> list = resultStructure.getFailures().getFailure();
Failure failure = new Failure();
failure.setFilereference(reference);
failure.setType(type);
failure.setComponent(component);
failure.setDescription(description);
if (details != null && details.length > 0) {
Details xmlDetails = new Details();
xmlDetails.getContent().add(Strings.join(Arrays.asList(details), "\n"));
failure.setDetails(xmlDetails);
}
if (maxResults == null || resultCount < maxResults) {
list.add(failure);
} else {
Details currentDetails = failure.getDetails();
if (currentDetails == null) {
currentDetails = new Details();
}
currentDetails.getContent().add(0, description);
failure.setDetails(currentDetails);
failure.setDescription("The number of results (" + resultCount + ") exceeded the maximum number that can be" +
" collected (" + maxResults + ").");
lastFailure = failure;
}
setSuccess(false);
}
/**
* Merge the failures from this ResultCollector into the given result collector. The maxResults specified in the
* "that" argument is respected.
*
* @param that the result collector to merge into
*
* @return that
*/
public ResultCollector mergeInto(ResultCollector that) {
for (Failure failure : getFailures()) {
ArrayList<String> details = new ArrayList<>();
if (failure.getDetails() != null) {
for (Object content : failure.getDetails().getContent()) {
details.add(content.toString());
}
}
that.addFailure(
failure.getFilereference(),
failure.getType(),
failure.getComponent(),
failure.getDescription(),
details.toArray(new String[details.size()]));
if (that.getTimestamp().before(this.getTimestamp())) {
that.setTimestamp(this.getTimestamp());
}
}
return that;
}
/**
* Get the list of failures. This method is only meant to be used for merging purposes
*
* @return the failures
*/
private List<Failure> getFailures() {
return Collections.unmodifiableList(
resultStructure.getFailures().getFailure());
}
/** Return the report as xml */
public String toReport() {
if (lastFailure != null) {
resultStructure.getFailures().getFailure().add(lastFailure);
}
try {
JAXBContext context = JAXBContext.newInstance(ObjectFactory.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
StringWriter writer = new StringWriter();
marshaller.marshal(resultStructure, writer);
return writer.toString();
} catch (JAXBException e) {
return null;
}
}
/**
* The timestamp of the event
*
* @return
*/
public Date getTimestamp() {
return resultStructure.getDate().toGregorianCalendar().getTime();
}
/**
* Timestamp the event that this is the result of
*
* @param timestamp
*/
public void setTimestamp(Date timestamp) {
resultStructure.setDate(format(timestamp));
}
public void setDuration(long durationInMS){
try {
resultStructure.setDuration(DatatypeFactory.newInstance().newDuration(durationInMS));
} catch (DatatypeConfigurationException e) {
throw new Error(e);
}
}
private XMLGregorianCalendar format(Date date) {
GregorianCalendar c = new GregorianCalendar();
c.setTime(date);
XMLGregorianCalendar date2 = null;
try {
date2 = DatatypeFactory.newInstance().newXMLGregorianCalendar(c);
} catch (DatatypeConfigurationException e) {
throw new Error(e);
}
return date2;
}
}