package net.codjo.segmentation.server.participant.context; import java.util.HashMap; import java.util.Map; import java.util.TreeSet; import net.codjo.segmentation.server.participant.context.SegmentationReport.Task; import net.codjo.util.time.SimpleStatistics; import net.codjo.util.time.Statistics; import net.codjo.util.time.SystemTimeSource; import net.codjo.util.time.TimeSource; import org.apache.commons.lang.mutable.MutableInt; /** * A simple implementation of {@link SegmentationReporter} that computes statistics per task. */ public class SimpleSegmentationReporter implements SegmentationReporter { private static final String REPORT_NAME = "Report"; protected static final String LINE_SEPARATOR = System.getProperty("line.separator"); private final TimeSource timeSource; public SimpleSegmentationReporter() { this(null); } public SimpleSegmentationReporter(TimeSource timeSource) { this.timeSource = SystemTimeSource.defaultIfNull(timeSource); } /** * {@inheritDoc} */ public final SegmentationReport create() { return create(timeSource); } protected SegmentationReport create(TimeSource timeSource) { return new SimpleReport(timeSource); } private class BaseObject { private final TimeSource timeSource; private final String name; private final long begin; protected SimpleReport report; private boolean closed = false; private boolean error = false; public BaseObject(TimeSource timeSource, String name) { this.timeSource = timeSource; this.name = name; this.begin = timeSource.getTime(); } protected final void setReport(SimpleReport report) { this.report = report; } public final void close() { if (!closed) { long duration = timeSource.getTime() - begin; closed = true; report.reportTaskTime(getName(), begin, duration, error); doAfterClose(duration); } } public final void reportError() { error = true; } protected void doAfterClose(long duration) { } public final String getName() { return name; } } protected class DefaultTask extends BaseObject implements Task { private DefaultTask(TimeSource timeSource, DefaultTask parent, String name, SimpleReport report) { super(timeSource, ((parent == null) ? "" : parent.getName() + '.') + name); setReport(report); } public final DefaultTask createTask(String name) { DefaultTask child = new DefaultTask(timeSource, this, name, report); return child; } @Override protected void doAfterClose(long duration) { } } static double roundTo3Digits(double value) { return (double)Math.round(value * 1000d) / 1000d; } protected class SimpleReport extends BaseObject implements SegmentationReport { private final Map<String, Statistics> stats = new HashMap<String, Statistics>(); private final Map<String, MutableInt> errorCounters = new HashMap<String, MutableInt>(); protected SimpleReport(TimeSource timeSource) { super(timeSource, REPORT_NAME); setReport(this); } public final Task createTask(String name) { return new DefaultTask(timeSource, null, name, this); } @Override protected final void doAfterClose(long duration) { StringBuilder s = new StringBuilder().append("Statistics for segmentation :").append(LINE_SEPARATOR); TreeSet<String> sortedTasks = new TreeSet<String>(stats.keySet()); for (String task : sortedTasks) { Statistics statistics = stats.get(task); if (REPORT_NAME.equals(task)) { s.append(REPORT_NAME).append(": totalTime=").append(statistics.getTotalTime()).append(" ms"); } else { appendStats(s, task, statistics, errorCounters.get(task), true); appendDetailedStats(s, task, statistics); } s.append(LINE_SEPARATOR); } LOGGER.info(s.toString()); } protected void appendDetailedStats(StringBuilder s, String task, Statistics statistics) { } protected Statistics createStatistics() { return new SimpleStatistics(); } protected void addTime(Statistics statistics, long begin, long timeToAdd) { ((SimpleStatistics)statistics).addTime(timeToAdd); } protected final void appendStats(StringBuilder s, String task, Statistics stats, MutableInt nbErrors, boolean reportErrors) { if (task != null) { s.append(task); } s.append(": "); if ((stats == null) || stats.isEmpty()) { s.append("no statistics"); } else { s.append("count=").append(stats.getCount()); s.append(", min=").append(stats.getMinTime()).append(" ms"); s.append(", max=").append(stats.getMaxTime()).append(" ms"); s.append(", totalTime=").append(stats.getTotalTime()).append(" ms"); s.append(", meanTime=").append(roundTo3Digits(stats.getMeanTime())).append(" ms"); s.append(", meanFrequency=") .append(roundTo3Digits(stats.getMeanFrequency())) .append(" operation(s)/second"); } if (reportErrors) { s.append("; "); if (nbErrors == null) { s.append("no error"); } else { s.append(nbErrors.intValue()).append(" error"); s.append((nbErrors.intValue() > 1) ? "s" : ""); } } } public final void reportTaskTime(String name, long begin, long duration, boolean error) { synchronized (stats) { Statistics s = stats.get(name); if (s == null) { s = createStatistics(); stats.put(name, s); } addTime(s, begin, duration); if (error) { MutableInt errorCounter = errorCounters.get(name); if (errorCounter == null) { errorCounter = new MutableInt(); errorCounters.put(name, errorCounter); } errorCounter.increment(); } } } } }