1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package com.ebay.carad.os.vitalsigns.listeners;
22
23 import java.awt.BasicStroke;
24 import java.awt.Color;
25 import java.io.FileWriter;
26 import java.io.IOException;
27 import java.text.SimpleDateFormat;
28 import java.util.Calendar;
29 import java.util.Date;
30 import java.util.GregorianCalendar;
31 import java.util.Iterator;
32 import java.util.List;
33 import java.util.Locale;
34 import java.util.Map;
35
36 import org.jfree.chart.ChartFactory;
37 import org.jfree.chart.JFreeChart;
38 import org.jfree.chart.renderer.category.GanttRenderer;
39 import org.jfree.data.category.IntervalCategoryDataset;
40 import org.jfree.data.gantt.Task;
41 import org.jfree.data.gantt.TaskSeries;
42 import org.jfree.data.gantt.TaskSeriesCollection;
43 import org.jfree.data.time.SimpleTimePeriod;
44
45 import com.ebay.carad.os.vitalsigns.IDashboardAgent;
46 import com.ebay.carad.os.vitalsigns.IDashboardReport;
47 import com.ebay.carad.os.vitalsigns.IReportingListenerContainer;
48 import com.ebay.carad.os.vitalsigns.ReportingException;
49 import com.ebay.carad.os.vitalsigns.dao.ISqlDAO;
50 import com.ebay.carad.os.vitalsigns.util.ChartUtil;
51 import com.ebay.carad.os.vitalsigns.util.ITimeConstants;
52
53 /***
54 * Create a chart for last 4 week's outages.
55 *
56 * TODO : this class still needs to be ported to work with the OS version of vitalsigns
57 *
58 * @author Jeremy Kraybill
59 * @author Jeremy Thomerson
60 * @version $Id$
61 */
62 public class UptimeReporter extends AbstractReportingListener implements IReportingListener, Cloneable {
63
64 private ISqlDAO mSqlDAO;
65
66 /***
67 * Basic constructor.
68 */
69 public UptimeReporter() {
70 }
71
72 /***
73 * Initializes a task series, with maintenance hours highlighted at top.
74 *
75 * @return an initialized series, ready to have new tasks added
76 */
77 public TaskSeries initializeSeries() {
78 TaskSeries s0 = new TaskSeries("Regular Operation");
79
80 Task tlt = new Task("Low-traffic hours",
81 new SimpleTimePeriod(date(0, 0), date(2, 0)));
82 tlt.setPercentComplete(1.0);
83 s0.add(tlt);
84
85 Task t1 = new Task("Maintenance hours",
86 new SimpleTimePeriod(date(2, 0), date(6, 0)));
87 t1.setPercentComplete(1.0);
88 s0.add(t1);
89
90 Task t2 = new Task("High-traffic hours",
91 new SimpleTimePeriod(date(6, 0), date(21, 0)));
92 t2.setPercentComplete(1.0);
93 s0.add(t2);
94
95 Task t3 = new Task(" Low-traffic hours",
96 new SimpleTimePeriod(date(21, 0), date(24, 0)));
97 t3.setPercentComplete(1.0);
98 s0.add(t3);
99
100 return s0;
101 }
102
103 /***
104 * Creates a sample dataset for the outage report.
105 *
106 * @param data a list of maps, representing the times of outages. Expect each map to contain "logtime" as a key.
107 * @return The dataset.
108 */
109 public IntervalCategoryDataset createDataset(List data) {
110
111 TaskSeries s0 = initializeSeries();
112
113
114 Calendar endDate = new GregorianCalendar();
115 endDate.set(Calendar.HOUR_OF_DAY, 23);
116 endDate.set(Calendar.MINUTE, 59);
117 endDate.set(Calendar.SECOND, 0);
118
119
120 Calendar curDate = new GregorianCalendar();
121 curDate.add(Calendar.DATE, -28);
122 curDate.set(Calendar.HOUR_OF_DAY, 0);
123 curDate.set(Calendar.MINUTE, 1);
124 curDate.set(Calendar.SECOND, 0);
125
126 SimpleDateFormat df = new SimpleDateFormat("MMM d, yyyy", Locale.getDefault());
127
128 Iterator outageIt = data.iterator();
129
130 Calendar curOutage = null;
131
132 if (outageIt.hasNext()) {
133 Map row = (Map) outageIt.next();
134 curOutage = new GregorianCalendar();
135 curOutage.setTimeInMillis(Long.parseLong(row.get("logtime").toString()));
136 }
137
138 while (!curDate.after(endDate)) {
139
140 Task dayTask = new Task(df.format(curDate.getTime()),
141 new SimpleTimePeriod(date(0,0), date(24,0)));
142 if ((curOutage == null) || (curOutage.get(Calendar.DAY_OF_YEAR) != curDate.get(Calendar.DAY_OF_YEAR))) {
143
144 dayTask.setPercentComplete(1.0);
145 } else {
146
147 Date startGreenMarker = date(curDate);
148 Calendar endRed = new GregorianCalendar();
149 while ((curOutage != null) && (curOutage.get(Calendar.DAY_OF_YEAR) == curDate.get(Calendar.DAY_OF_YEAR))) {
150 Date endGreenMarker = date(curOutage);
151 long lastOutageLong = curOutage.getTimeInMillis();
152 long endRedLong = lastOutageLong;
153 while ((curOutage != null) && (curOutage.get(Calendar.DAY_OF_YEAR) == curDate.get(Calendar.DAY_OF_YEAR)) && (curOutage.getTimeInMillis() - 180000 <= lastOutageLong)) {
154
155 endRedLong = curOutage.getTimeInMillis() + 180000;
156 lastOutageLong = curOutage.getTimeInMillis();
157 if (outageIt.hasNext()) {
158 Map row = (Map) outageIt.next();
159 curOutage.setTimeInMillis(Long.parseLong(row.get("logtime").toString()));
160 } else {
161 curOutage = null;
162 }
163 }
164 Task green = new Task("",
165 new SimpleTimePeriod(startGreenMarker, endGreenMarker));
166 green.setPercentComplete(1.0);
167 endRed.setTimeInMillis(endRedLong);
168 startGreenMarker = date(endRed);
169 Task red = new Task("",
170 new SimpleTimePeriod(endGreenMarker, startGreenMarker));
171 red.setPercentComplete(0.0);
172 dayTask.addSubtask(green);
173 dayTask.addSubtask(red);
174 }
175 Task green = new Task("",
176 new SimpleTimePeriod(date(endRed), date(24, 00)));
177 green.setPercentComplete(1.0);
178 dayTask.addSubtask(green);
179 }
180 s0.add(dayTask);
181 curDate.add(Calendar.DATE, 1);
182 }
183
184 TaskSeriesCollection collection = new TaskSeriesCollection();
185 collection.add(s0);
186
187 return collection;
188 }
189
190 /***
191 * Utility method for creating <code>Date</code> objects.
192 *
193 * @param inHour the hour
194 * @param inMinute the minute
195 * @return a date.
196 */
197 private static Date date(int inHour, int inMinute) {
198 int day = 1;
199 int curHour = inHour;
200 if (curHour > 23) {
201 day++;
202 curHour = curHour - 24;
203 }
204 Calendar calendar = Calendar.getInstance();
205 calendar.set(2004, 1, day, curHour, inMinute);
206 Date result = calendar.getTime();
207 return result;
208 }
209
210 /***
211 * Utility method for creating a simple date from a calendar object.
212 *
213 * @param calendar the calendar to use
214 * @return a date.
215 */
216 private static Date date(Calendar calendar) {
217 return date(calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE));
218 }
219 /***
220 * Creates a chart.
221 *
222 * @param dataset the dataset
223 * @return the chart
224 */
225 private JFreeChart createChart(final IntervalCategoryDataset dataset) {
226 final JFreeChart chart = ChartFactory.createGanttChart(
227 null,
228 null,
229 "Time",
230 dataset,
231 false,
232 false,
233 false
234 );
235 chart.getCategoryPlot().getDomainAxis().setMaximumCategoryLabelWidthRatio(10.0f);
236 final GanttRenderer renderer = (GanttRenderer)chart.getCategoryPlot().getRenderer();
237 renderer.setSeriesPaint(0, Color.WHITE);
238 renderer.setSeriesStroke(0, new BasicStroke(0));
239 renderer.setOutlineStroke(new BasicStroke(0));
240 renderer.setStartPercent(0.0);
241 renderer.setEndPercent(1.0);
242 return chart;
243 }
244
245 /***
246 * Basic reporting method. Generates a graph of name reportX.jpg, and a corresponding HTML file.
247 *
248 * @param config my config
249 * @param agent my agent
250 * @return an empty string
251 * @throws IOException if bad things happen
252 */
253 public String report(IDashboardReport config, IDashboardAgent agent) throws IOException {
254 String dest = agent.getDestinationPath();
255
256 long oneMonthAgo = System.currentTimeMillis() - ITimeConstants.MONTH;
257 List data = mSqlDAO.findBySqlQuery("select logtime from reportdata where ReportId=" + config.getID() + " AND logtime >= " + oneMonthAgo + " and data IS NOT NULL ORDER BY logtime ASC;");
258 IntervalCategoryDataset dataset = createDataset(data);
259 JFreeChart chart = createChart(dataset);
260 chart.setBackgroundPaint(Color.WHITE);
261
262 String chartBase = dest + "report";
263 ChartUtil.saveChart(chart, chartBase + config.getID() + ".jpg", 800, 600, 1.0f);
264 writeHTML(config, chartBase, agent);
265
266 return "";
267 }
268
269 private void writeHTML(IDashboardReport config, String chartBase, IReportingListenerContainer agent) throws IOException {
270 FileWriter fw = new FileWriter(chartBase + config.getID() + ".html");
271 fw.write("<META HTTP-EQUIV=Refresh CONTENT=\"900; URL=right.html\">\n" +
272 "<BODY BGCOLOR=\"#FFFFFF\">\n" +
273 "<TABLE BORDER=0 CELLPADDING=20 CELLSPACING=0>\n" +
274 " <TR>\n" +
275 " <TD COLSPAN=4 VALIGN=CENTER ALIGN=CENTER>\n" +
276 " <FONT FACE=\"VERDANA\" SIZE=6><B>" + config.getTitle() + "</B><BR>\n");
277 fw.write("<FONT SIZE=4><B>" + config.getTitle() + "</B><BR>" + config.getSubTitle() + "<BR><B><P><FONT SIZE=2>");
278 fw.write("<BR><IMG SRC=\"report" + config.getID() + ".jpg\"><P>\n");
279 fw.write("</TD></TR>" + " <TR><TD COLSPAN=4 VALIGN=CENTER ALIGN=CENTER><FONT FACE=\"VERDANA\" SIZE=1>The sort order of this report is " + config.getSortOrder() + ".</TD></TR>" + " <TR>\n" +
280 " <TD COLSPAN=4 VALIGN=CENTER ALIGN=CENTER>\n" +
281 " <FONT FACE=\"VERDANA\" SIZE=1>This page auto-reloads every fifteen minutes.<BR>Percentage values reflect % change week/month/year.<P>\n" +
282 " A gap across all reports indicates that the logging utility was reset. <BR>\n" +
283 " A gap in ONE chart indicates that an error occurred when trying to gather the data in question. <BR>\n" +
284 " Direct questions <a href=\"mailto:jkraybill@ebay.com\">here</a>.\n" +
285 " This page last generated " + new Date().toString() + "</TD>\n </TR></TABLE>\n");
286 fw.close();
287 }
288
289 public Object clone() throws CloneNotSupportedException {
290 return super.clone();
291 }
292
293 public void setSqlDAO(ISqlDAO sqlDAO) {
294 mSqlDAO = sqlDAO;
295 }
296
297 public void reportRan(IDashboardAgent agent, IDashboardReport report) {
298 try {
299 report(report, agent);
300 } catch (IOException ex) {
301 throw new ReportingException("Exception while writing report: " + ex.getMessage(), ex);
302 }
303 }
304
305 }