001 /* 002 @license.text@ 003 */ 004 package biz.hammurapi.metrics.persistent; 005 006 import java.io.File; 007 import java.io.IOException; 008 import java.sql.SQLException; 009 import java.util.ArrayList; 010 import java.util.Collection; 011 import java.util.Date; 012 import java.util.HashMap; 013 import java.util.Iterator; 014 015 import javax.xml.parsers.FactoryConfigurationError; 016 import javax.xml.parsers.ParserConfigurationException; 017 import javax.xml.transform.TransformerException; 018 import javax.xml.transform.stream.StreamResult; 019 020 import org.jfree.chart.ChartFactory; 021 import org.jfree.chart.JFreeChart; 022 import org.jfree.chart.plot.PlotOrientation; 023 import org.jfree.data.time.TimeSeries; 024 import org.jfree.data.time.TimeSeriesCollection; 025 import org.jfree.data.xy.DefaultHighLowDataset; 026 import org.jfree.data.xy.OHLCDataset; 027 028 import biz.hammurapi.config.MapContext; 029 import biz.hammurapi.config.RuntimeConfigurationException; 030 import biz.hammurapi.metrics.MetricsException; 031 import biz.hammurapi.sql.SQLProcessor; 032 import biz.hammurapi.sql.hypersonic.HypersonicServerDataSource; 033 import biz.hammurapi.util.ClassTransformerFactory; 034 import biz.hammurapi.util.Visitor; 035 036 037 /** 038 * @author Pavel Vlasov 039 * @version $Revision: 1.2 $ 040 */ 041 public class PeriodVisualizer { 042 private AbstractPeriod period; 043 044 /** 045 * Constructor from array of metric names 046 * @param processor Processor to retrieve metrics from the database 047 * @param metrics Metrics to be retrieved 048 * @param from Start of metrics period 049 * @param to End of metrics period 050 * @param cacheMode Cache mode 051 * @param outputFormat FORMAT_PNG or FORMAT_JPG 052 * @param width Chart width 053 * @param height Chart height 054 * @param maxSlices Maximum number of slices on chart. Series having more slices will be downsliced - adjacent slices will be merged. 055 * @throws SQLException 056 */ 057 public PeriodVisualizer(AbstractPeriod period) { 058 this.period=period; 059 } 060 061 private JFreeChart intencityChart; 062 063 /** 064 * @return 065 */ 066 public JFreeChart intensityChart() { 067 if (intencityChart==null) { 068 TimeSeriesCollection tsc=new TimeSeriesCollection(); 069 TimeSeries timeSeries = new TimeSeries(normalizeName(period.getName()), SliceTimePeriod.class); 070 SliceTimePeriod sp=null; 071 for (int i=0; i<period.getSlices().length; i++) { 072 sp=new SliceTimePeriod(period.getSlices()[i], sp); 073 timeSeries.add(sp, period.getSlices()[i].getMeasurements()); 074 } 075 076 tsc.addSeries(timeSeries); 077 078 intencityChart = ChartFactory.createXYBarChart( 079 "Intensity (measurements/slice)", // Title 080 "Time", // X-Axis label 081 true, 082 "Intensity", // Y-Axis label 083 tsc, // Dataset 084 PlotOrientation.VERTICAL, 085 true, // Show legend 086 true, 087 true 088 ); 089 } 090 091 return intencityChart; 092 } 093 094 private JFreeChart totalChart; 095 096 /** 097 * @return 098 */ 099 public JFreeChart totalChart() { 100 if (totalChart==null) { 101 TimeSeriesCollection tsc=new TimeSeriesCollection(); 102 TimeSeries timeSeries = new TimeSeries(normalizeName(period.getName()), SliceTimePeriod.class); 103 SliceTimePeriod sp=null; 104 for (int i=0; i<period.getSlices().length; i++) { 105 sp=new SliceTimePeriod(period.getSlices()[i], sp); 106 timeSeries.add(sp, period.getSlices()[i].getTotal()); 107 } 108 109 tsc.addSeries(timeSeries); 110 111 totalChart = ChartFactory.createXYBarChart( 112 "Total (sum of values per slice)", // Title 113 "Time", // X-Axis label 114 true, 115 "Total", // Y-Axis label 116 tsc, // Dataset 117 PlotOrientation.VERTICAL, 118 true, // Show legend 119 true, 120 true 121 ); 122 } 123 124 return totalChart; 125 } 126 127 private JFreeChart averageChart; 128 129 private String normalizeName(String name) { 130 return name.length()>25 ? "Category" : name; 131 } 132 133 /** 134 * @return 135 */ 136 public JFreeChart averageChart() { 137 if (averageChart==null) { 138 TimeSeriesCollection tsc=new TimeSeriesCollection(); 139 TimeSeries timeSeries = new TimeSeries(normalizeName(period.getName()), SliceTimePeriod.class); 140 SliceTimePeriod sp=null; 141 for (int i=0; i<period.getSlices().length; i++) { 142 sp=new SliceTimePeriod(period.getSlices()[i], sp); 143 timeSeries.add(sp, period.getSlices()[i].getTotal()/period.getSlices()[i].getMeasurements()); 144 } 145 146 tsc.addSeries(timeSeries); 147 148 averageChart = ChartFactory.createTimeSeriesChart( 149 "Average", // Title 150 "Time", // X-Axis label 151 "Average", // Y-Axis label 152 tsc, // Dataset 153 true, // Show legend 154 true, 155 true 156 ); 157 } 158 159 return averageChart; 160 } 161 162 private JFreeChart childrenIntencityChart; 163 164 /** 165 * @return 166 * @throws SQLException 167 */ 168 public JFreeChart childrenIntensityChart() throws SQLException { 169 if (childrenIntencityChart==null && period.getSubCategories().size()>1) { 170 TimeSeriesCollection tsc=new TimeSeriesCollection(); 171 Iterator it=period.getSubCategories().iterator(); 172 while (it.hasNext()) { 173 Period p=(Period) it.next(); 174 TimeSeries timeSeries = new TimeSeries(period.getLabel(p.getId()), SliceTimePeriod.class); 175 SliceTimePeriod sp=null; 176 for (int i=0; i<p.getSlices().length; i++) { 177 sp=new SliceTimePeriod(p.getSlices()[i], sp); 178 timeSeries.add(sp, p.getSlices()[i].getMeasurements()); 179 } 180 tsc.addSeries(timeSeries); 181 } 182 183 childrenIntencityChart = ChartFactory.createTimeSeriesChart( 184 "Intensity (measurements/time)", // Title 185 "Time", // X-Axis label 186 "Intensity", // Y-Axis label 187 tsc, // Dataset 188 true, // Show legend 189 true, 190 true 191 ); 192 } 193 194 return childrenIntencityChart; 195 } 196 197 private JFreeChart childrenTotalChart; 198 199 /** 200 * @return 201 * @throws SQLException 202 */ 203 public JFreeChart childrenTotalChart() throws SQLException { 204 if (childrenTotalChart==null && period.getSubCategories().size()>1) { 205 TimeSeriesCollection tsc=new TimeSeriesCollection(); 206 Iterator it=period.getSubCategories().iterator(); 207 while (it.hasNext()) { 208 Period p=(Period) it.next(); 209 TimeSeries timeSeries = new TimeSeries(period.getLabel(p.getId()), SliceTimePeriod.class); 210 SliceTimePeriod sp=null; 211 for (int i=0; i<p.getSlices().length; i++) { 212 sp=new SliceTimePeriod(p.getSlices()[i], sp); 213 timeSeries.add(sp, p.getSlices()[i].getTotal()); 214 } 215 tsc.addSeries(timeSeries); 216 } 217 218 childrenTotalChart = ChartFactory.createTimeSeriesChart( 219 "Total (sum of values per slice)", // Title 220 "Time", // X-Axis label 221 "Total", // Y-Axis label 222 tsc, // Dataset 223 true, // Show legend 224 true, 225 true 226 ); 227 } 228 229 return childrenTotalChart; 230 } 231 232 private JFreeChart childrenAverageChart; 233 234 /** 235 * @return 236 * @throws SQLException 237 */ 238 public JFreeChart childrenAverageChart() throws SQLException { 239 if (childrenAverageChart==null && period.getSubCategories().size()>1) { 240 TimeSeriesCollection tsc=new TimeSeriesCollection(); 241 Iterator it=period.getSubCategories().iterator(); 242 while (it.hasNext()) { 243 Period p=(Period) it.next(); 244 TimeSeries timeSeries = new TimeSeries(period.getLabel(p.getId()), SliceTimePeriod.class); 245 SliceTimePeriod sp=null; 246 for (int i=0; i<p.getSlices().length; i++) { 247 sp=new SliceTimePeriod(p.getSlices()[i], sp); 248 timeSeries.add(sp, p.getSlices()[i].getTotal()/p.getSlices()[i].getMeasurements()); 249 } 250 tsc.addSeries(timeSeries); 251 } 252 253 childrenAverageChart = ChartFactory.createTimeSeriesChart( 254 "Average", // Title 255 "Time", // X-Axis label 256 "Average", // Y-Axis label 257 tsc, // Dataset 258 true, // Show legend 259 true, 260 true 261 ); 262 } 263 return childrenAverageChart; 264 } 265 266 private JFreeChart mainChart; 267 268 /** 269 * @param i 270 * @return 271 */ 272 public JFreeChart mainChart() { 273 if (mainChart==null) { 274 Slice[] periodSlices = period.getSlices(); 275 int size = periodSlices.length; 276 277 Date[] dates=new Date[size]; 278 double[] higs=new double[size]; 279 double[] lows=new double[size]; 280 double[] opens=new double[size]; 281 double[] closes=new double[size]; 282 double[] volumes=new double[size]; 283 284 for (int k=0; k<size; k++) { 285 Slice slice=periodSlices[k]; 286 dates[k]=new Date(slice.getTimeFrom()); 287 higs[k]=slice.getMaxValue(); 288 lows[k]=slice.getMinValue(); 289 double avg = slice.getMeasurements()==0 ? 0 : slice.getTotal()/slice.getMeasurements(); 290 opens[k]=Math.max(slice.getMeasurements()==0 ? avg : avg-slice.getDeviation(), slice.getMinValue()); 291 closes[k]=Math.min(slice.getMeasurements()==0 ? avg : opens[k]+slice.getDeviation(), slice.getMaxValue()); 292 volumes[k]=slice.getTotal(); 293 } 294 295 OHLCDataset dataSet=new DefaultHighLowDataset( 296 normalizeName(period.getName()), 297 dates, 298 higs, 299 lows, 300 opens, 301 closes, 302 volumes); 303 304 mainChart = ChartFactory.createCandlestickChart( 305 normalizeName(period.getName()), 306 "Time", 307 "Value", 308 dataSet, 309 true 310 ); 311 } 312 313 return mainChart; 314 } 315 316 /** 317 * @return Returns the period. 318 */ 319 public AbstractPeriod getPeriod() { 320 return period; 321 } 322 323 private Collection children; 324 325 public Collection getChildren() throws SQLException { 326 if (children==null) { 327 children=new ArrayList(); 328 Iterator it=period.getSubCategories().iterator(); 329 while (it.hasNext()) { 330 children.add(new PeriodVisualizer((Period) it.next())); 331 } 332 } 333 334 return children; 335 } 336 337 /** 338 * @param root 339 */ 340 public static void generateHtmlDoc(AbstractPeriod root, final File outputDir, final int width, final int height) { 341 root.accept(new Visitor() { 342 343 ClassTransformerFactory ctf=new ClassTransformerFactory(null, null, true); 344 345 public boolean visit(Object target) { 346 try { 347 AbstractPeriod period = (AbstractPeriod) target; 348 ctf.transform(period, "category", null, null, null, null, new StreamResult(new File(outputDir, target instanceof Period ? "period_"+((Period) period).getId()+".html" : "index.html"))); 349 // DOMUtils.serialize(period, "category", new File(outputDir, "period_"+period.getId()+".xml")); 350 PeriodVisualizer visualizer=new PeriodVisualizer(period); 351 String periodId = period instanceof Period ? String.valueOf(((Period) period).getId()) : ((SyntheticPeriod) period).getId(); 352 353 ChartHelper chartHelper = new ChartHelper(ChartHelper.FORMAT_JPG, width, height); 354 chartHelper.output(visualizer.averageChart(), new File(outputDir, "average_"+periodId+".jpg")); 355 chartHelper.output(visualizer.childrenAverageChart(), new File(outputDir, "childrenAverage_"+periodId+".jpg")); 356 chartHelper.output(visualizer.childrenIntensityChart(), new File(outputDir, "childrenIntensity_"+periodId+".jpg")); 357 chartHelper.output(visualizer.childrenTotalChart(), new File(outputDir, "childrenTotal_"+periodId+".jpg")); 358 chartHelper.output(visualizer.intensityChart(), new File(outputDir, "intensity_"+periodId+".jpg")); 359 chartHelper.output(visualizer.mainChart(), new File(outputDir, "main_"+periodId+".jpg")); 360 chartHelper.output(visualizer.totalChart(), new File(outputDir, "total_"+periodId+".jpg")); 361 } catch (ParserConfigurationException e) { 362 throw new RuntimeConfigurationException(e); 363 } catch (FactoryConfigurationError e) { 364 throw new RuntimeConfigurationException(e); 365 } catch (IOException e) { 366 throw new RuntimeConfigurationException(e); 367 } catch (TransformerException e) { 368 throw new RuntimeConfigurationException(e); 369 } catch (SQLException e) { 370 throw new RuntimeConfigurationException(e); 371 } 372 return true; 373 } 374 375 }); 376 } 377 378 public static void main(String args[]) throws Exception { 379 File outputDir=new File("_MetricDebug"); 380 if (outputDir.exists()) { 381 if (!outputDir.isDirectory()) { 382 throw new MetricsException(outputDir.getAbsolutePath()+" in not a directory"); 383 } 384 } else { 385 if (!outputDir.mkdirs()) { 386 throw new MetricsException("Cannot create directory "+outputDir.getAbsolutePath()); 387 } 388 } 389 390 // Generation of html's 391 SQLProcessor processor=new SQLProcessor(new HypersonicServerDataSource("localhost/metrics", null), new MapContext(new HashMap())); 392 new MetricNormalizer(processor).normalize(); 393 PeriodFactory factory=new PeriodFactory(processor); 394 MetricLocator locator=new MetricLocator(processor); 395 AbstractPeriod root=factory.getPeriod(locator.find("poc.wsusjxla3b3a147.send size").intValue(), 0, Long.MAX_VALUE, 20, true); 396 PeriodVisualizer.generateHtmlDoc(root, outputDir, 500, 300); 397 398 } 399 }