001package org.javasimon.console.plugin; 002 003import java.io.IOException; 004 005import org.javasimon.Simon; 006import org.javasimon.Stopwatch; 007import org.javasimon.callback.timeline.StopwatchTimeRange; 008import org.javasimon.callback.timeline.TimeRange; 009import org.javasimon.callback.timeline.Timeline; 010import org.javasimon.callback.timeline.TimelineCallback; 011import org.javasimon.callback.timeline.TimelineSample; 012import org.javasimon.clock.SimonClock; 013import org.javasimon.console.ActionContext; 014import org.javasimon.console.SimonCallbacks; 015import org.javasimon.console.action.DetailHtmlBuilder; 016import org.javasimon.console.action.DetailPlugin; 017import org.javasimon.console.html.HtmlResourceType; 018import org.javasimon.console.json.ArrayJS; 019import org.javasimon.console.json.ObjectJS; 020import org.javasimon.console.text.StringifierFactory; 021 022/** 023 * Detail plugin to display {@link TimelineCallback} information 024 */ 025public class TimelineDetailPlugin extends DetailPlugin { 026 027 /** 028 * Message: Callback not registered 029 */ 030 public static final String NO_CALLBACK_MESSAGE = "Timeline callback not registered"; 031 /** 032 * Message: Data not found in Simon 033 */ 034 private static final String NO_DATA_MESSAGE = "No data available"; 035 /** 036 * Simon attribute name used to retrieved timeline information 037 */ 038 private final String attributeName; 039 public TimelineDetailPlugin(String pluginId, String pluginTitle, String attributeName) { 040 super(pluginId, pluginTitle); 041 this.attributeName = attributeName; 042 // Google Chart 043 addResource("https://www.google.com/jsapi", HtmlResourceType.JS); 044 addResource("js/jquery-dataTables.js", HtmlResourceType.JS); 045 addResource("js/javasimon-timelinePlugin.js", HtmlResourceType.JS); 046 addResource("css/javasimon-timelinePlugin.css", HtmlResourceType.CSS); 047 } 048 public TimelineDetailPlugin() { 049 this("timeline", "Timeline", TimelineCallback.TIMELINE_ATTRIBUTE_NAME); 050 } 051 052 /** 053 * Indicate that this plugin only applies on Stopwatches. 054 */ 055 @Override 056 public boolean supports(Simon simon) { 057 return simon instanceof Stopwatch; 058 } 059 060 /** 061 * Indicate whether {@link TimelineCallback} was registered in manager 062 */ 063 private boolean isTimelineCallbackRegistered(ActionContext context) { 064 return SimonCallbacks.getCallbackByType(context.getManager(), TimelineCallback.class) != null; 065 } 066 private TimelineSample getData(Simon simon) { 067 return ((Timeline) simon.getAttribute(attributeName)).sample(); 068 } 069 070 /** 071 * Generate an HTML message row 072 */ 073 private void htmlMessage(DetailHtmlBuilder htmlBuilder, String message) throws IOException { 074 htmlBuilder.beginRow() 075 .labelCell("Message").valueCell(" colspan=\"3\"", message) 076 .endRow(); 077 } 078 @Override 079 public DetailHtmlBuilder executeHtml(ActionContext context, DetailHtmlBuilder htmlBuilder, StringifierFactory htmlStringifierFactory, Simon simon) throws IOException { 080 if (isTimelineCallbackRegistered(context)) { 081 TimelineSample timelineSample = getData(simon); 082 if (timelineSample == null) { 083 htmlMessage(htmlBuilder, NO_DATA_MESSAGE); 084 } else { 085 htmlBuilder.beginRow() 086 .labelCell("Capacity") 087 .valueCell(htmlStringifierFactory.toString(timelineSample.getCapacity())) 088 .labelCell("Width") 089 .valueCell(htmlStringifierFactory.toString(timelineSample.getWidth()* SimonClock.NANOS_IN_MILLIS,"Time")) 090 .endRow(); 091 htmlBuilder.beginRow().labelCell("Evolution").beginValueCell(" colspan=\"3\""); 092 htmlBuilder.begin("table").begin("thead") 093 .beginRow().labelCell("Start").labelCell("End"); 094 if (simon instanceof Stopwatch) { 095 htmlBuilder.labelCell("Counter").labelCell("Total").labelCell("Min").labelCell("Mean").labelCell("Last").labelCell("Max").labelCell("Std. Dev."); 096 } 097 htmlBuilder.endRow().end("thead").begin("tbody"); 098 for(TimeRange timeRange:timelineSample.getTimeRanges()) { 099 htmlBuilder.beginRow() 100 .valueCell(htmlStringifierFactory.toString(timeRange.getStartTimestamp(),"Date")) 101 .valueCell(htmlStringifierFactory.toString(timeRange.getEndTimestamp(),"Date")); 102 if (timeRange instanceof StopwatchTimeRange) { 103 StopwatchTimeRange sTimeRange=(StopwatchTimeRange) timeRange; 104 htmlBuilder 105 .valueCell(htmlStringifierFactory.toString(sTimeRange.getCounter())) 106 .valueCell(htmlStringifierFactory.toString(sTimeRange.getTotal(),"Time")) 107 .valueCell(htmlStringifierFactory.toString(sTimeRange.getMin(),"Time")) 108 .valueCell(htmlStringifierFactory.toString(sTimeRange.getMean(),"Time")) 109 .valueCell(htmlStringifierFactory.toString(sTimeRange.getLast(),"Time")) 110 .valueCell(htmlStringifierFactory.toString(sTimeRange.getMax(),"Time")) 111 .valueCell(htmlStringifierFactory.toString(sTimeRange.getStandardDeviation(),"Time")); 112 } 113 htmlBuilder.endRow(); 114 } 115 htmlBuilder.end("tbody").end("table"); 116 htmlBuilder.endValueCell().endRow(); 117 } 118 } else { 119 htmlMessage(htmlBuilder, NO_CALLBACK_MESSAGE); 120 } 121 return htmlBuilder; 122 } 123 124 /** 125 * Generate a JSON message attribute 126 */ 127 private ObjectJS jsonMessage(String message, StringifierFactory jsonStringifierFactory) { 128 ObjectJS timelineJS = new ObjectJS(); 129 timelineJS.setSimpleAttribute("message", message, jsonStringifierFactory.getStringifier(String.class)); 130 return timelineJS; 131 } 132 133 @Override 134 public ObjectJS executeJson(ActionContext context, StringifierFactory jsonStringifierFactory, Simon simon) { 135 ObjectJS timelineJS; 136 if (isTimelineCallbackRegistered(context)) { 137 TimelineSample timelineSample = getData(simon); 138 if (timelineSample == null) { 139 timelineJS = jsonMessage(NO_DATA_MESSAGE, jsonStringifierFactory); 140 } else { 141 timelineJS = ObjectJS.create(timelineSample, jsonStringifierFactory); 142 timelineJS.setAttribute("timeRanges", ArrayJS.create(timelineSample.getTimeRanges(), jsonStringifierFactory)); 143 } 144 } else { 145 timelineJS = jsonMessage(NO_CALLBACK_MESSAGE, jsonStringifierFactory); 146 } 147 return timelineJS; 148 } 149 150}