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}