View Javadoc

1   /*
2    * The contents of this file are subject to the terms 
3    * of the Common Development and Distribution License 
4    * (the "License").  You may not use this file except 
5    * in compliance with the License.
6    * 
7    * You can obtain a copy of the license at 
8    * http://www.sun.com/cddl/cddl.html. 
9    * See the License for the specific language governing 
10   * permissions and limitations under the License.
11   * 
12   * When distributing Covered Code, include this CDDL 
13   * HEADER in each file and include the License file at 
14   * license.txt.  If applicable, add the following below 
15   * this CDDL HEADER, with the fields enclosed by brackets 
16   * "[]" replaced with your own identifying information: 
17   * Portions Copyright [yyyy] [name of copyright owner]
18   * 
19   * Portions Copyright 2004 eBay, Inc.
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() { // noop
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 		// we want to set the stopping point of the chart to be the end of the current day.
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 		// and now set the starting point of the chart to be the beginning of the day (12:01 AM) four weeks ago.
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 			// do this day's duties.
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 				// no outage today.
144 				dayTask.setPercentComplete(1.0);
145 			} else {
146 				// there was an outage today.
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 						// all part of the same outage (3 minutes apart)
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,		// chart title
228             null,		// domain axis label
229             "Time",		// range axis label
230             dataset,	// data
231             false,		// include legend
232             false,		// tooltips
233             false		// urls
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 }