package com.servoy.j2db.server.shared;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import com.servoy.j2db.util.Pair;
import com.servoy.j2db.util.UUID;
/**
* Keeps a list off last 200 most expensive actions (e.g. sql, method calls) to be viewed in a UI.
*
* @author jblok
*/
public class PerformanceData extends PerformanceAggregator
{
private final Map<UUID, PerformanceTiming> startedTimings = new HashMap<UUID, PerformanceTiming>();
// stack because for example an showForm modal dialog could execute other actions and then when modal
// is closed sub-actions might still happen and they need to point to the correct parent action
private final Stack<UUID> startedTimingUUIDsStack = new Stack<>();
public PerformanceData(int maxEntriesToKeep)
{
super(maxEntriesToKeep);
}
public synchronized UUID startAction(String action, long start_ms, int type, String clientUUID)
{
if (maxEntriesToKeep == IPerfomanceRegistry.OFF) return null;
PerformanceTiming timing = new PerformanceTiming(action, type, start_ms, clientUUID, maxEntriesToKeep);
startedTimingUUIDsStack.push(timing.getUuid());
startedTimings.put(timing.getUuid(), timing);
return timing.getUuid();
}
public synchronized UUID startAction(String action, long start_ms, int type)
{
return startAction(action, start_ms, type, null);
}
public synchronized void intervalAction(UUID uuid)
{
if (maxEntriesToKeep == IPerfomanceRegistry.OFF) return;
PerformanceTiming timing = startedTimings.get(uuid);
if (timing != null) timing.setIntervalTime();
}
public synchronized void endAction(UUID uuid)
{
if (maxEntriesToKeep == IPerfomanceRegistry.OFF) return;
PerformanceTiming timing = startedTimings.remove(uuid);
if (timing != null) addTiming(timing.getAction(), timing.getIntervalTimeMS(), timing.getRunningTimeMS(), timing.getType(), timing.toMap());
startedTimingUUIDsStack.pop();
}
// currently we can have/need only one layer of nesting/sub-actions (sub-actions cannot be accessed right now by the outside world to continue nesting furter)
public synchronized Pair<UUID, UUID> startSubAction(String action, long start_ms, int type, String clientUUID)
{
if (maxEntriesToKeep == IPerfomanceRegistry.OFF) return null;
if (startedTimingUUIDsStack.isEmpty()) return null; // probably a Servoy internal service API call that gets called outside any user method; ignore
UUID lastStartedTimingUUID = startedTimingUUIDsStack.peek();
PerformanceTiming lastStartedTiming = startedTimings.get(lastStartedTimingUUID);
UUID subTimingUUID = null;
if (lastStartedTiming != null)
{
subTimingUUID = lastStartedTiming.startAction(action, start_ms, type, clientUUID);
}
return new Pair<>(lastStartedTimingUUID, subTimingUUID);
}
public synchronized void endSubAction(Pair<UUID, UUID> subActionUUIDs)
{
if (maxEntriesToKeep == IPerfomanceRegistry.OFF) return;
if (subActionUUIDs == null) return; // probably a Servoy internal service API call that gets called outside any user method; ignore
PerformanceTiming timingWithSubAction = startedTimings.get(subActionUUIDs.getLeft());
if (timingWithSubAction != null)
{
timingWithSubAction.endAction(subActionUUIDs.getRight());
}
}
protected static class TimeComparator implements Comparator<PerformanceTimingAggregate>
{
public int compare(PerformanceTimingAggregate o1, PerformanceTimingAggregate o2)
{
long t1 = o1.getTotalIntervalTimeMS();
long t2 = o2.getTotalIntervalTimeMS();
if (t1 == t2)
{
return o1.getAction().compareTo(o2.getAction());
}
return (int)(t2 - t1);
}
}
public synchronized PerformanceTiming[] getStartedActions()
{
return startedTimings.values().toArray(new PerformanceTiming[startedTimings.size()]);
}
}