package org.xins.server;
import java.util.Map;
import java.util.TimeZone;
import java.util.TreeMap;
import org.apache.log4j.NDC;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xins.common.text.DateConverter;
import org.xins.common.xml.ElementFormatter;
class FunctionStatistics {
private static final String NOT_AVAILABLE = "N/A";
private static final TimeZone TIME_ZONE = TimeZone.getDefault();
FunctionStatistics() {
_successful = new Statistic();
_unsuccessful = new Statistic();
_notModified = new Statistic();
_errorCodeStatistics = new TreeMap();
}
private final Statistic _successful;
private final Statistic _unsuccessful;
private final Statistic _notModified;
private final Map<String, Statistic> _errorCodeStatistics;
final synchronized void recordCall(long start, FunctionResult xinsResult) {
long duration = System.currentTimeMillis() - start;
if (xinsResult instanceof NotModifiedResult) {
_notModified.recordCall(start, duration);
} else if (xinsResult.getErrorCode() == null) {
_successful.recordCall(start, duration);
} else {
_unsuccessful.recordCall(start, duration);
String errorCode = xinsResult.getErrorCode();
Statistic errorCodeStat = _errorCodeStatistics.get(errorCode);
if (errorCodeStat == null) {
errorCodeStat = new Statistic();
}
errorCodeStat.recordCall(start, duration);
_errorCodeStatistics.put(errorCode, errorCodeStat);
}
}
final synchronized void resetStatistics() {
_successful.reset();
_unsuccessful.reset();
_notModified.reset();
_errorCodeStatistics.clear();
}
public synchronized Element getSuccessfulElement() {
return _successful.getElement("successful", null);
}
public synchronized boolean hasNotModified() {
return _notModified._calls > 0;
}
public synchronized Element getNotModifiedElement() {
return _notModified.getElement("not-modified", null);
}
public synchronized Element[] getUnsuccessfulElement(boolean detailed) {
if (!detailed || _errorCodeStatistics.isEmpty()) {
Element[] result = new Element[1];
result[0] = _unsuccessful.getElement("unsuccessful", null);
return result;
} else {
Element[] result = new Element[_errorCodeStatistics.size()];
int i = 0;
for (String nextErrorCode : _errorCodeStatistics.keySet()) {
Statistic nextStat = _errorCodeStatistics.get(nextErrorCode);
result[i] = nextStat.getElement("unsuccessful", nextErrorCode);
i++;
}
return result;
}
}
private static final class Statistic {
private int _calls;
private long _lastStart;
private long _lastDuration;
private String _lastContextId;
private long _duration;
private long _min = Long.MAX_VALUE;
private long _minStart;
private String _minContextId;
private long _max;
private long _maxStart;
private String _maxContextId;
private Statistic() {
_min = Long.MAX_VALUE;
}
public synchronized void recordCall(long start, long duration) {
_lastStart = start;
_lastDuration = duration;
_calls++;
_duration += duration;
_min = _min > duration ? duration : _min;
_max = _max < duration ? duration : _max;
_minStart = (_min == duration) ? start : _minStart;
_maxStart = (_max == duration) ? start : _maxStart;
_lastContextId = NDC.peek();
if (_min == duration) {
_minContextId = _lastContextId;
}
if (_max == duration) {
_maxContextId = _lastContextId;
}
}
public synchronized Element getElement(String name, String errorCode) {
String average;
String min;
String minStart;
String max;
String maxStart;
String lastStart;
String lastDuration;
if (_calls == 0) {
average = NOT_AVAILABLE;
min = NOT_AVAILABLE;
minStart = NOT_AVAILABLE;
max = NOT_AVAILABLE;
maxStart = NOT_AVAILABLE;
lastStart = NOT_AVAILABLE;
lastDuration = NOT_AVAILABLE;
} else if (_duration == 0) {
average = "0";
min = String.valueOf(_min);
minStart = DateConverter.toDateString(TIME_ZONE, _minStart);
max = String.valueOf(_max);
maxStart = DateConverter.toDateString(TIME_ZONE, _maxStart);
lastStart = DateConverter.toDateString(TIME_ZONE, _lastStart);
lastDuration = String.valueOf(_lastDuration);
} else {
average = String.valueOf(_duration / _calls);
min = String.valueOf(_min);
minStart = DateConverter.toDateString(TIME_ZONE, _minStart);
max = String.valueOf(_max);
maxStart = DateConverter.toDateString(TIME_ZONE, _maxStart);
lastStart = DateConverter.toDateString(TIME_ZONE, _lastStart);
lastDuration = String.valueOf(_lastDuration);
}
Element element = ElementFormatter.createMainElement(name);
Document doc = element.getOwnerDocument();
element.setAttribute("count", String.valueOf(_calls));
element.setAttribute("average", average);
if (errorCode != null) {
element.setAttribute("errorcode", errorCode);
}
Element minElem = doc.createElement("min");
minElem.setAttribute("start", minStart);
minElem.setAttribute("duration", min);
minElem.setAttribute("contextId", _minContextId);
element.appendChild(minElem);
Element maxElem = doc.createElement("max");
maxElem.setAttribute("start", maxStart);
maxElem.setAttribute("duration", max);
maxElem.setAttribute("contextId", _maxContextId);
element.appendChild(maxElem);
Element lastElem = doc.createElement("last");
lastElem.setAttribute("start", lastStart);
lastElem.setAttribute("duration", lastDuration);
lastElem.setAttribute("contextId", _lastContextId);
element.appendChild(lastElem);
return element;
}
public synchronized void reset() {
_calls = 0;
_lastStart = 0L;
_lastDuration = 0L;
_duration = 0L;
_min = Long.MAX_VALUE;
_minStart = 0L;
_max = 0L;
_maxStart = 0L;
}
}
}