Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
cdc.perfs.core.io.PerfsXml Maven / Gradle / Ivy
package cdc.perfs.core.io;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import cdc.io.compress.Compressor;
import cdc.io.data.Child;
import cdc.io.data.Element;
import cdc.io.data.NodeType;
import cdc.io.data.util.AbstractResourceLoader;
import cdc.io.xml.AbstractStAXLoader;
import cdc.io.xml.AbstractStAXParser;
import cdc.io.xml.XmlWriter;
import cdc.perfs.api.MeasureLevel;
import cdc.perfs.api.RuntimeManager;
import cdc.perfs.api.RuntimeProbe;
import cdc.perfs.api.Source;
import cdc.perfs.api.SourceLevel;
import cdc.perfs.core.Context;
import cdc.perfs.core.Environment;
import cdc.perfs.core.Measure;
import cdc.perfs.core.MeasureStatus;
import cdc.perfs.core.snapshot.SnapshotContext;
import cdc.perfs.core.snapshot.SnapshotEnvironment;
import cdc.perfs.core.snapshot.SnapshotMeasure;
import cdc.util.events.ProgressController;
import cdc.util.events.ProgressSupplier;
import cdc.util.lang.Checks;
import cdc.util.lang.FailureReaction;
/**
* Environment XML serialization / deserialization.
*
* @author Damien Carbonne
*
*/
public final class PerfsXml {
private static final char SEPARATOR = ':';
private static final String ALIVE = "alive";
private static final String BEGIN = "begin";
private static final String CONTEXT = "context";
private static final String COMPACT_CODE = "c";
private static final String COMPACT_MEASURE = "m";
private static final String DETAILS = "details";
private static final String ELAPSED_NANOS = "elapsed-nanos";
private static final String END = "end";
private static final String FORMAT = "format";
private static final String ID = "id";
private static final String KIND = "kind";
private static final String LEVEL = "level";
private static final String MAX_LEVEL = "max-level";
private static final String MEASURE = "measure";
private static final String NAME = "name";
private static final String PERFS = "perfs";
private static final String REF_DATE = "ref-date";
private static final String REF_NANOS = "ref-nanos";
private static final String SOURCE = "source";
private static final String STATUS = "status";
private PerfsXml() {
}
static int indexOfFirstNonDigit(String s,
int from) {
for (int index = from; index < s.length(); index++) {
final char c = s.charAt(index);
if (c < '0' || c > '9') {
return index;
}
}
return -1;
}
/**
* Enumeration of XML formats.
*
* @author Damien Carbonne
*
*/
public enum Format {
/**
* Standard format is quite verbose and easy to read.
*/
STANDARD,
/**
* Compact mode is, ... compact.
*
* It can, use 3 to 4 times less space that STANDARD mode.
* It is more cryptic, but easier to read than binary format.
*/
COMPACT;
boolean isCompact() {
return this == COMPACT;
}
}
static SimpleDateFormat getDateFormat() {
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
}
static SimpleDateFormat getAlternateDateFormat() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
/**
* Environment XML serialization.
*
* @author Damien Carbonne
*
*/
public static class Printer {
private static final Logger LOGGER = LogManager.getLogger(PerfsXml.Printer.class);
private final Format format;
private static final Source SRC = RuntimeManager.getSource(Printer.class);
private final Map sources = new HashMap<>();
public Printer(Format format) {
Checks.isNotNull(format, FORMAT);
this.format = format;
}
public Printer() {
this(Format.STANDARD);
}
public Format getFormat() {
return format;
}
public void save(Environment environment,
String filename,
Compressor compressor,
ProgressController controller) throws IOException {
final RuntimeProbe probe = RuntimeManager.createProbe(SRC, MeasureLevel.INFO);
probe.start("save(" + filename + ", " + compressor + ")");
try (final XmlWriter writer = new XmlWriter(filename, compressor)) {
if (format.isCompact()) {
writer.setIndentString("");
writer.setEnabled(XmlWriter.Feature.PRETTY_PRINT, false);
} else {
writer.setIndentString(" ");
writer.setEnabled(XmlWriter.Feature.PRETTY_PRINT, true);
}
printDocument(environment, writer, controller);
} finally {
probe.stop();
}
}
public void printDocument(Environment environment,
XmlWriter writer,
ProgressController controller) throws IOException {
LOGGER.trace("printDocument(...)");
final ProgressSupplier progressSupplier = new ProgressSupplier(controller);
writer.reset();
writer.beginDocument();
printEnvironment(environment, writer, progressSupplier);
writer.endDocument();
}
private void printEnvironment(Environment environment,
XmlWriter writer,
ProgressSupplier progressSupplier) throws IOException {
LOGGER.trace("printEnvironment(...) {}", getFormat());
progressSupplier.setTotal((long) environment.getSources().size() + environment.getMeasuresCount());
writer.beginElement(PERFS);
if (format.isCompact()) {
writer.addAttribute(FORMAT, getFormat().name());
}
writer.addAttribute(KIND, environment.getKind().name());
writer.addAttribute(REF_DATE, PerfsXml.getDateFormat().format(Date.from(environment.getReferenceInstant())));
writer.addAttribute(REF_NANOS, environment.getReferenceNanos());
writer.addAttribute(ELAPSED_NANOS, environment.getElapsedNanos());
sources.clear();
for (final Source source : environment.getSources()) {
printSource(source, writer, progressSupplier);
}
for (final Context context : environment.getContexts()) {
printContext(context, writer, progressSupplier);
}
writer.endElement();
}
private void printSource(Source source,
XmlWriter writer,
ProgressSupplier progressSupplier) throws IOException {
LOGGER.trace("printSource(...)");
progressSupplier.checkCancelled();
progressSupplier.incrementValue();
final short id = (short) (sources.size() + 1);
sources.put(source, id);
writer.beginElement(SOURCE);
if (format.isCompact()) {
writer.addAttribute(ID, sources.get(source));
}
writer.addAttribute(NAME, source.getName());
writer.addAttribute(MAX_LEVEL, source.getMaxLevel().name());
writer.endElement();
}
private void printContext(Context context,
XmlWriter writer,
ProgressSupplier progressSupplier) throws IOException {
LOGGER.trace("printContext(...)");
writer.beginElement(CONTEXT);
writer.addAttribute(ID, context.getId());
writer.addAttribute(NAME, context.getName());
writer.addAttribute(ALIVE, context.isAlive());
for (int index = 0; index < context.getRootMeasuresCount(); index++) {
printMeasure(context.getRootMeasure(index), writer, progressSupplier);
}
writer.endElement();
}
private void printMeasure(Measure measure,
XmlWriter writer,
ProgressSupplier progressSupplier) throws IOException {
LOGGER.trace("printMeasure(...)");
progressSupplier.checkCancelled();
progressSupplier.incrementValue();
if (getFormat() == Format.STANDARD) {
writer.beginElement(MEASURE);
writer.addAttribute(SOURCE, measure.getSource().getName());
writer.addAttribute(STATUS, measure.getStatus().name());
writer.addAttribute(LEVEL, measure.getLevel().name());
if (measure.getDetails() != null && !measure.getDetails().isEmpty()) {
writer.addAttribute(DETAILS, measure.getDetails());
}
writer.addAttribute(BEGIN, measure.getAbsoluteBeginNanos());
if (measure.getStatus() != MeasureStatus.RUNNING) {
writer.addAttribute(END, measure.getAbsoluteEndNanos());
}
} else { // COMPACT
writer.beginElement(COMPACT_MEASURE);
final StringBuilder builder = new StringBuilder();
builder.append(sources.get(measure.getSource()));
builder.append(measure.getStatus().getCode());
builder.append(measure.getRelativeBeginNanos());
builder.append(measure.getLevel().getCode());
if (measure.getStatus() != MeasureStatus.RUNNING) {
builder.append(measure.getElapsedNanos());
}
builder.append(SEPARATOR);
if (measure.getDetails() != null && !measure.getDetails().isEmpty()) {
builder.append(measure.getDetails());
}
writer.addAttribute(COMPACT_CODE, builder.toString());
}
Measure child = measure.getFirstChild();
while (child != null) {
printMeasure(child, writer, progressSupplier);
child = child.getNextSibling();
}
writer.endElement();
}
}
/**
* Environment XML deserialization.
*
* @author Damien Carbonne
*
*/
public static class DataLoader extends AbstractResourceLoader {
private static final Source SRC = RuntimeManager.getSource(DataLoader.class);
private final Map sources = new HashMap<>();
private Format format;
public DataLoader(FailureReaction reaction) {
super(reaction);
}
private Date toDate(String s) {
try {
return getDateFormat().parse(s);
} catch (final ParseException e) {
try {
return getAlternateDateFormat().parse(s);
} catch (final ParseException ex) {
getLogger().error("Failed to parse date: '{}'", s);
return null;
}
}
}
private static long count(Element root) {
final RuntimeProbe probe = RuntimeManager.createProbe(SRC, MeasureLevel.INFO);
probe.start("count()");
try {
long result = 0L;
for (final Element child : root.getChildren(Element.class)) {
switch (child.getName()) {
case SOURCE:
result++;
break;
case CONTEXT:
result += countMeasures(child);
break;
default:
break;
}
}
return result;
} finally {
probe.stop();
}
}
private static long countMeasures(Element element) {
long result = 0L;
for (final Child child : element.getChildren()) {
if (child.getType() == NodeType.ELEMENT) {
final Element candidate = (Element) child;
if (MEASURE.equals(candidate.getName()) || COMPACT_MEASURE.equals(candidate.getName())) {
result++;
result += countMeasures(candidate);
}
}
}
return result;
}
@Override
protected SnapshotEnvironment loadRoot(Element root) throws IOException {
final RuntimeProbe probe = RuntimeManager.createProbe(SRC, MeasureLevel.INFO);
probe.start("loadRoot(...)");
try {
getProgressSupplier().checkCancelled();
getProgressSupplier().setTotal(count(root));
getProgressSupplier().setValue(0L);
final long refNanos = root.getAttributeAsLong(REF_NANOS, 0);
final Date refDate = toDate(root.getAttributeValue(REF_DATE, "1970-01-01T00:00:00"));
final long elapsedNanos = root.getAttributeAsLong(ELAPSED_NANOS, 0);
final Instant refInstant = refDate == null ? null : refDate.toInstant();
this.format = root.getAttributeAsEnum(FORMAT, Format.class, Format.STANDARD);
final SnapshotEnvironment environment = new SnapshotEnvironment(refNanos, refInstant, elapsedNanos);
for (final Element child : root.getChildren(Element.class)) {
switch (child.getName()) {
case SOURCE:
loadSource(environment, child);
break;
case CONTEXT:
loadContext(environment, child);
break;
default:
unexpectedElement(child, SOURCE, CONTEXT);
break;
}
}
return environment;
} finally {
probe.stop();
}
}
private void loadSource(SnapshotEnvironment environment,
Element element) throws IOException {
getProgressSupplier().checkCancelled();
getProgressSupplier().incrementValue();
final String name = element.getAttributeValue(NAME, null);
final SourceLevel maxLevel = element.getAttributeAsEnum(MAX_LEVEL, SourceLevel.class, null);
final Source source = environment.getSource(name);
source.setMaxLevel(maxLevel);
if (format.isCompact()) {
final short id = element.getAttributeAsShort(ID, (short) -1);
sources.put(id, source);
}
}
private void loadContext(SnapshotEnvironment environment,
Element element) throws IOException {
final int id = element.getAttributeAsInt(ID, 0);
final String name = element.getAttributeValue(NAME, null);
final boolean alive = element.getAttributeAsBoolean(ALIVE, false);
final SnapshotContext context = environment.createContext(id, name, alive);
SnapshotMeasure previous = null;
if (format == Format.STANDARD) {
for (final Element child : element.getChildren(Element.class)) {
if (MEASURE.equals(child.getName())) {
previous = loadMeasure(context, null, 0, previous, child);
}
}
} else {
for (final Element child : element.getChildren(Element.class)) {
if (COMPACT_MEASURE.equals(child.getName())) {
previous = loadMeasure(context, null, 0, previous, child);
}
}
}
}
private SnapshotMeasure loadMeasure(SnapshotContext context,
SnapshotMeasure parent,
int parentDepth,
SnapshotMeasure previous,
Element element) throws IOException {
getProgressSupplier().checkCancelled();
getProgressSupplier().incrementValue();
if (format == Format.STANDARD) {
final String sourceName = element.getAttributeValue(SOURCE, null);
final MeasureStatus status = element.getAttributeAsEnum(STATUS, MeasureStatus.class, null);
final MeasureLevel level = element.getAttributeAsEnum(LEVEL, MeasureLevel.class, null);
final String details = element.getAttributeValue(DETAILS, null);
final long begin = element.getAttributeAsLong(BEGIN, 0);
final long end = element.getAttributeAsLong(END, 0);
final SnapshotMeasure measure =
context.createMeasure(parent,
parentDepth,
previous,
context.getEnvironment().getSource(sourceName),
details,
begin,
end,
status,
level);
SnapshotMeasure prev = null;
for (final Element child : element.getChildren(Element.class)) {
if (MEASURE.equals(child.getName())) {
prev = loadMeasure(context, measure, parentDepth + 1, prev, child);
}
}
return measure;
} else {
final String code = element.getAttributeValue(COMPACT_CODE);
// source id + status code + relative begin + level code (+ elapsed nanos) + separator (+ details)
final int statusPos = indexOfFirstNonDigit(code, 0);
final int levelPos = indexOfFirstNonDigit(code, statusPos + 1);
final int separatorPos = code.indexOf(SEPARATOR, levelPos + 1);
final short sourceId = Short.parseShort(code.substring(0, statusPos));
final char statusCode = code.charAt(statusPos);
final MeasureStatus status = MeasureStatus.ENCODER.decode(statusCode);
final char levelCode = code.charAt(levelPos);
final MeasureLevel level = MeasureLevel.ENCODER.decode(levelCode);
final long relativeBegin = Long.parseLong(code.substring(statusPos + 1, levelPos));
final long begin = relativeBegin + context.getEnvironment().getReferenceNanos();
final long elapsed = status == MeasureStatus.RUNNING
? 0L
: Long.parseLong(code.substring(levelPos + 1, separatorPos));
final long end = begin + elapsed;
final String details = separatorPos == code.length() - 1
? null
: code.substring(separatorPos + 1);
final SnapshotMeasure measure =
context.createMeasure(parent,
parentDepth,
previous,
sources.get(sourceId),
details,
begin,
end,
status,
level);
SnapshotMeasure prev = null;
for (final Element child : element.getChildren(Element.class)) {
if (COMPACT_MEASURE.equals(child.getName())) {
prev = loadMeasure(context, measure, parentDepth + 1, prev, child);
}
}
return measure;
}
}
}
public static class StAXLoader extends AbstractStAXLoader {
public StAXLoader(FailureReaction reaction) {
super((reader,
systemId) -> new Parser(reader, systemId, reaction));
}
private static class Parser extends AbstractStAXParser {
private final Map sources = new HashMap<>();
private Format format;
protected Parser(XMLStreamReader reader,
String systemId,
FailureReaction reaction) {
super(reader, systemId, reaction);
}
private Date toDate(String s) {
try {
return getDateFormat().parse(s);
} catch (final ParseException e) {
try {
return getAlternateDateFormat().parse(s);
} catch (final ParseException ex) {
getLogger().error("Failed to parse date: '{}'", s);
return null;
}
}
}
@Override
protected SnapshotEnvironment parse() throws XMLStreamException {
nextTag();
if (isStartElement(PERFS)) {
final SnapshotEnvironment result = parseEnvironment();
next();
return result;
} else {
throw unexpectedEvent();
}
}
private SnapshotEnvironment parseEnvironment() throws XMLStreamException {
final long refNanos = getAttributeAsLong(REF_NANOS, 0);
final Date refDate = toDate(getAttributeValue(REF_DATE, "1970-01-01T00:00:00"));
final long elapsedNanos = getAttributeAsLong(ELAPSED_NANOS, 0);
final Instant refInstant = refDate == null ? null : refDate.toInstant();
this.format = getAttributeAsEnum(FORMAT, Format.class, Format.STANDARD);
final SnapshotEnvironment environment = new SnapshotEnvironment(refNanos, refInstant, elapsedNanos);
nextTag();
while (reader.isStartElement()) {
if (isStartElement(SOURCE)) {
parseSource(environment);
} else if (isStartElement(CONTEXT)) {
parseContext(environment);
} else {
throw unexpectedEvent();
}
nextTag();
}
return environment;
}
private void parseSource(SnapshotEnvironment environment) throws XMLStreamException {
final String name = getAttributeValue(NAME, null);
final SourceLevel maxLevel = getAttributeAsEnum(MAX_LEVEL, SourceLevel.class, null);
final Source source = environment.getSource(name);
source.setMaxLevel(maxLevel);
if (format.isCompact()) {
final short id = getAttributeAsShort(ID, (short) -1);
sources.put(id, source);
}
nextTag();
}
private void parseContext(SnapshotEnvironment environment) throws XMLStreamException {
final int id = getAttributeAsInt(ID, 0);
final String name = getAttributeValue(NAME, null);
final boolean alive = getAttributeAsBoolean(ALIVE, false);
final SnapshotContext context = environment.createContext(id, name, alive);
SnapshotMeasure previous = null;
nextTag();
if (format == Format.STANDARD) {
while (isStartElement(MEASURE)) {
previous = parseStandardMeasure(context, null, 0, previous);
nextTag();
}
} else {
while (isStartElement(COMPACT_MEASURE)) {
previous = parseCompactMeasure(context, null, 0, previous);
nextTag();
}
}
}
private SnapshotMeasure parseStandardMeasure(SnapshotContext context,
SnapshotMeasure parent,
int parentDepth,
SnapshotMeasure previous) throws XMLStreamException {
final String sourceName = getAttributeValue(SOURCE, null);
final MeasureStatus status = getAttributeAsEnum(STATUS, MeasureStatus.class, null);
final MeasureLevel level = getAttributeAsEnum(LEVEL, MeasureLevel.class, null);
final String details = getAttributeValue(DETAILS, null);
final long begin = getAttributeAsLong(BEGIN, 0);
final long end = getAttributeAsLong(END, 0);
final SnapshotMeasure measure =
context.createMeasure(parent,
parentDepth,
previous,
context.getEnvironment().getSource(sourceName),
details,
begin,
end,
status,
level);
SnapshotMeasure prev = null;
nextTag();
while (isStartElement(MEASURE)) {
prev = parseStandardMeasure(context, measure, parentDepth + 1, prev);
nextTag();
}
return measure;
}
private SnapshotMeasure parseCompactMeasure(SnapshotContext context,
SnapshotMeasure parent,
int parentDepth,
SnapshotMeasure previous) throws XMLStreamException {
final String code = getAttributeValue(COMPACT_CODE, null);
// source id + status code + relative begin + level code (+ elapsed nanos) + separator (+ details)
final int statusPos = indexOfFirstNonDigit(code, 0);
final int levelPos = indexOfFirstNonDigit(code, statusPos + 1);
final int separatorPos = code.indexOf(SEPARATOR, levelPos + 1);
final short sourceId = Short.parseShort(code.substring(0, statusPos));
final char statusCode = code.charAt(statusPos);
final MeasureStatus status = MeasureStatus.ENCODER.decode(statusCode);
final char levelCode = code.charAt(levelPos);
final MeasureLevel level = MeasureLevel.ENCODER.decode(levelCode);
final long relativeBegin = Long.parseLong(code.substring(statusPos + 1, levelPos));
final long begin = relativeBegin + context.getEnvironment().getReferenceNanos();
final long elapsed = status == MeasureStatus.RUNNING
? 0L
: Long.parseLong(code.substring(levelPos + 1, separatorPos));
final long end = begin + elapsed;
final String details = separatorPos == code.length() - 1
? null
: code.substring(separatorPos + 1);
final SnapshotMeasure measure =
context.createMeasure(parent,
parentDepth,
previous,
sources.get(sourceId),
details,
begin,
end,
status,
level);
SnapshotMeasure prev = null;
nextTag();
while (isStartElement(COMPACT_MEASURE)) {
prev = parseCompactMeasure(context, measure, parentDepth + 1, prev);
nextTag();
}
return measure;
}
}
}
}