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.io.PerfsXml Maven / Gradle / Ivy
package cdc.perfs.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 org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import cdc.perfs.Context;
import cdc.perfs.Environment;
import cdc.perfs.Measure;
import cdc.perfs.MeasureStatus;
import cdc.perfs.api.MeasureLevel;
import cdc.perfs.api.RuntimeProbe;
import cdc.perfs.api.Source;
import cdc.perfs.runtime.RuntimeEnvironment;
import cdc.perfs.snapshot.SnapshotContext;
import cdc.perfs.snapshot.SnapshotEnvironment;
import cdc.perfs.snapshot.SnapshotMeasure;
import cdc.util.compress.Compressor;
import cdc.util.data.Element;
import cdc.util.data.util.AbstractResourceLoader;
import cdc.util.lang.Checks;
import cdc.util.lang.FailureReaction;
import cdc.util.lang.IntMasks;
import cdc.util.xml.XmlWriter;
/**
* 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_DETAILS = "d";
// private static final String COMPACT_BEGIN = "b";
// private static final String COMPACT_END = "e";
private static final String COMPACT_MEASURE = "m";
// private static final String COMPACT_SOURCE = "s";
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() {
}
/**
* 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;
}
}
protected static SimpleDateFormat getDateFormat() {
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
}
protected 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 int features = 0;
private final Format format;
private static final Source SRC = RuntimeEnvironment.getInstance().getSource(Printer.class);
private final Map sources = new HashMap<>();
public enum Feature {
DEBUG
}
public Printer(Format format) {
Checks.isNotNull(format, "format");
this.format = format;
}
public Printer() {
this(Format.STANDARD);
}
public Format getFormat() {
return format;
}
public void setEnabled(Feature feature,
boolean enabled) {
features = IntMasks.setEnabled(features, feature, enabled);
}
public boolean isEnabled(Feature feature) {
return IntMasks.isEnabled(features, feature);
}
public void save(Environment environment,
String filename,
Compressor compressor) throws IOException {
final RuntimeProbe probe = RuntimeEnvironment.getInstance().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);
} finally {
probe.stop();
}
}
public void printDocument(Environment environment,
XmlWriter writer) throws IOException {
LOGGER.trace("printDocument(...)");
writer.reset();
writer.beginDocument();
print(environment, writer);
writer.endDocument();
}
public void print(Environment environment,
XmlWriter writer) throws IOException {
LOGGER.trace("print'Environment(...) " + getFormat());
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()) {
print(source, writer);
}
for (final Context context : environment.getContexts()) {
print(context, writer);
}
writer.endElement();
}
private void print(Source source,
XmlWriter writer) throws IOException {
LOGGER.trace("print'Source(...)");
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 print(Context context,
XmlWriter writer) throws IOException {
LOGGER.trace("print'Context(...)");
writer.beginElement(CONTEXT);
writer.addAttribute(ID, context.getId());
writer.addAttribute(NAME, context.getName());
writer.addAttribute(ALIVE, context.isAlive());
if (isEnabled(Feature.DEBUG)) {
writer.addAttribute("height", context.getHeight());
}
for (int index = 0; index < context.getRootMeasuresCount(); index++) {
print(context.getRootMeasure(index), writer);
}
writer.endElement();
}
private void print(Measure measure,
XmlWriter writer) throws IOException {
LOGGER.trace("print'Measure(...)");
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 (isEnabled(Feature.DEBUG)) {
writer.addAttribute("height", measure.getHeight());
}
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());
// writer.addAttribute(COMPACT_SOURCE, sources.get(measure.getSource()));
// writer.addAttribute(STATUS, measure.getStatus().getCode());
// writer.addAttribute(LEVEL, measure.getLevel().getCode());
// if (measure.getDetails() != null && !measure.getDetails().isEmpty()) {
// writer.addAttribute(COMPACT_DETAILS, measure.getDetails());
// }
// writer.addAttribute(COMPACT_BEGIN, measure.getRelativeBeginNanos());
// if (measure.getStatus() != MeasureStatus.RUNNING) {
// writer.addAttribute(COMPACT_END, measure.getElapsedNanos());
// }
}
Measure child = measure.getFirstChild();
while (child != null) {
print(child, writer);
child = child.getNextSibling();
}
writer.endElement();
}
}
/**
* Environment XML deserialization.
*
* @author Damien Carbonne
*
*/
public static class Loader extends AbstractResourceLoader {
private static final Source SRC = RuntimeEnvironment.getInstance().getSource(Loader.class);
private final Map sources = new HashMap<>();
private Format format;
public Loader(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;
}
}
}
@Override
protected SnapshotEnvironment loadRoot(Element root) {
final RuntimeProbe probe = RuntimeEnvironment.getInstance().createProbe(SRC, MeasureLevel.INFO);
probe.start("loadRoot(...)");
try {
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) {
final String name = element.getAttributeValue(NAME, null);
final MeasureLevel maxLevel = element.getAttributeAsEnum(MAX_LEVEL, MeasureLevel.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) {
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 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;
}
private SnapshotMeasure loadMeasure(SnapshotContext context,
SnapshotMeasure parent,
int parentDepth,
SnapshotMeasure previous,
Element element) {
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;
}
}
}
}