All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cdc.perfs.core.io.PerfsXml Maven / Gradle / Ivy

There is a newer version: 0.52.0
Show newest version
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; } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy