1 package com.google.code.jetm.maven;
2
3 import java.io.File;
4 import java.io.FileInputStream;
5 import java.io.FileNotFoundException;
6 import java.io.InputStreamReader;
7 import java.nio.charset.Charset;
8 import java.text.DecimalFormat;
9 import java.util.ArrayList;
10 import java.util.Collection;
11 import java.util.Collections;
12 import java.util.HashMap;
13 import java.util.LinkedHashMap;
14 import java.util.LinkedList;
15 import java.util.List;
16 import java.util.Locale;
17 import java.util.Map;
18 import java.util.Map.Entry;
19
20 import org.apache.commons.io.FileUtils;
21 import org.apache.commons.io.IOUtils;
22 import org.apache.commons.io.filefilter.TrueFileFilter;
23 import org.apache.maven.doxia.sink.Sink;
24 import org.apache.maven.doxia.siterenderer.Renderer;
25 import org.apache.maven.project.MavenProject;
26 import org.apache.maven.reporting.AbstractMavenReport;
27 import org.apache.maven.reporting.MavenReportException;
28 import org.codehaus.plexus.util.StringUtils;
29
30 import com.google.code.jetm.maven.data.AggregateSummary;
31 import com.google.code.jetm.maven.data.TimeUnit;
32 import com.google.code.jetm.maven.util.AggregateComparator;
33 import com.google.code.jetm.maven.util.XmlIOFileFilter;
34 import com.google.code.jetm.reporting.AggregateBinder;
35 import com.google.code.jetm.reporting.xml.XmlAggregateBinder;
36
37 import etm.core.aggregation.Aggregate;
38
39
40
41
42
43
44
45
46
47
48 public class TimingReportMojo extends AbstractMavenReport {
49
50
51
52
53
54 private File[] timings;
55
56
57
58
59
60
61
62 private String inputEncoding;
63
64
65
66
67
68
69
70
71
72
73
74 private String timeUnit;
75
76
77
78
79
80
81
82
83 private String outputDirectory;
84
85
86
87
88
89
90 private MavenProject project;
91
92
93
94
95
96
97 private Renderer siteRenderer;
98
99
100
101
102
103
104
105
106 private File buildDirectory;
107
108 private final AggregateBinder binder = new XmlAggregateBinder();
109
110 private DecimalFormat decimalFormatter = new DecimalFormat("0.00");
111
112 private final XmlIOFileFilter xmlFileFilter = new XmlIOFileFilter();
113
114 @Override
115 public boolean canGenerateReport() {
116 return !getTimingFiles().isEmpty();
117 }
118
119
120
121
122 public String getOutputName() {
123 return "jetm-timing-report";
124 }
125
126
127
128
129 public String getName(Locale locale) {
130 return "JETM Timing Report";
131 }
132
133
134
135
136 public String getDescription(Locale locale) {
137 return "A collective report of all JETM timings that were collected and rendered.";
138 }
139
140
141
142
143 protected void executeReport(Locale locale) throws MavenReportException {
144 final Map<File, List<Aggregate>> aggregates = getAggregates();
145 final List<AggregateSummary> summaries = aggregate(aggregates);
146 Collections.sort(summaries);
147
148 final Sink sink = getSink();
149 try {
150 sink.head();
151 sink.title();
152 sink.text(getName(locale));
153 sink.title_();
154 sink.head_();
155
156 sink.body();
157 sink.sectionTitle1();
158 sink.text(getName(locale));
159 sink.sectionTitle1_();
160
161 if (summaries.isEmpty()) {
162 sink.text(" There are no JETM timings available for reporting.");
163 return;
164 }
165
166 sink.sectionTitle2();
167 sink.text("Summary");
168 sink.sectionTitle2_();
169
170 sink.text("This is a summary, by measurement name, of the measurements taken.");
171
172 print(sink, summaries);
173
174 sink.sectionTitle2();
175 sink.text("File Breakdown");
176 sink.sectionTitle2_();
177
178 sink.text("This is a list of, per XML file, the measurements taken.");
179
180 for (Entry<File, List<Aggregate>> entry : aggregates.entrySet()) {
181 sink.sectionTitle3();
182 sink.text(entry.getKey().getName());
183 sink.sectionTitle3_();
184
185 print(sink, entry.getValue());
186 }
187 } finally {
188 sink.body_();
189
190 sink.flush();
191 sink.close();
192 }
193 }
194
195
196
197
198 protected String getOutputDirectory() {
199 return outputDirectory;
200 }
201
202
203
204
205 protected MavenProject getProject() {
206 return project;
207 }
208
209
210
211
212 protected Renderer getSiteRenderer() {
213 return siteRenderer;
214 }
215
216
217
218
219
220
221
222
223
224
225 private List<AggregateSummary> aggregate(Map<File, List<Aggregate>> aggregates) {
226 if (aggregates.isEmpty())
227 return Collections.emptyList();
228
229 final Map<String, AggregateSummary> summaries = new HashMap<String, AggregateSummary>();
230 for (List<Aggregate> aggregateList : aggregates.values())
231 for (Aggregate aggregate : aggregateList) {
232 final String name = aggregate.getName();
233 if (!summaries.containsKey(name))
234 summaries.put(name, new AggregateSummary(name));
235
236 final AggregateSummary summary = summaries.get(name);
237 summary.add(aggregate);
238 }
239
240 final List<AggregateSummary> summaryList = new ArrayList<AggregateSummary>(summaries.size());
241 for (AggregateSummary summary : summaries.values())
242 summaryList.add(summary);
243
244 return summaryList;
245 }
246
247
248
249
250
251
252
253
254
255
256
257
258
259 private Map<File, List<Aggregate>> getAggregates() throws MavenReportException {
260 final Map<File, List<Aggregate>> aggregates = new LinkedHashMap<File, List<Aggregate>>();
261 for (File file : getTimingFiles()) {
262 final List<Aggregate> aggregateList = new LinkedList<Aggregate>();
263 InputStreamReader reader;
264 try {
265 reader = new InputStreamReader(new FileInputStream(file), getInputCharset());
266 } catch (FileNotFoundException e) {
267 throw new MavenReportException("File not found: " + file, e);
268 }
269 try {
270 final Collection<Aggregate> unbound = binder.unbind(reader);
271 if (unbound.isEmpty())
272 continue;
273 aggregateList.addAll(unbound);
274 } finally {
275 IOUtils.closeQuietly(reader);
276 }
277 aggregates.put(file, aggregateList);
278 }
279 return aggregates;
280 }
281
282
283
284
285
286
287 private Charset getInputCharset() {
288 return StringUtils.isBlank(inputEncoding) ? Charset.defaultCharset() : Charset.forName(inputEncoding);
289 }
290
291
292
293
294
295
296 private TimeUnit getTimeUnit() {
297 return TimeUnit.fromMojoAbbreviation(timeUnit);
298 }
299
300
301
302
303
304
305 private File[] getTimingDirectories() {
306 return timings == null ? new File[] { new File(buildDirectory, "jetm") } : timings;
307 }
308
309
310
311
312
313
314 private List<File> getTimingFiles() {
315 final List<File> timingFiles = new ArrayList<File>();
316 for (File timingDirectory : getTimingDirectories()) {
317 if (!timingDirectory.exists())
318 continue;
319
320 timingFiles.addAll(FileUtils.listFiles(timingDirectory, xmlFileFilter, TrueFileFilter.TRUE));
321 }
322
323 return timingFiles;
324 }
325
326
327
328
329
330
331
332
333
334
335 private void print(Sink sink, Collection<? extends Aggregate> aggregates) {
336 final TimeUnit timeUnit = getTimeUnit();
337
338 sink.table();
339 sink.tableRows(null, false);
340 sink.tableRow();
341 tableHeaderCell(sink, "Name");
342 tableHeaderCell(sink, "Average (" + timeUnit.getDisplayName() + ")");
343 tableHeaderCell(sink, "Measurements");
344 tableHeaderCell(sink, "Minimum (" + timeUnit.getDisplayName() + ")");
345 tableHeaderCell(sink, "Maximum (" + timeUnit.getDisplayName() + ")");
346 tableHeaderCell(sink, "Total (" + timeUnit.getDisplayName() + ")");
347 sink.tableRow_();
348
349 final List<? extends Aggregate> sortedAggregates = new ArrayList<Aggregate>(aggregates);
350 Collections.sort(sortedAggregates, new AggregateComparator());
351
352 for (Aggregate aggregate : sortedAggregates) {
353 sink.tableRow();
354 tableCell(sink, aggregate.getName());
355 tableCell(sink, decimalFormatter.format(timeUnit.fromMilliseconds(aggregate.getTotal() / aggregate.getMeasurements())));
356 tableCell(sink, Long.toString(aggregate.getMeasurements()));
357 tableCell(sink, decimalFormatter.format(timeUnit.fromMilliseconds(aggregate.getMin())));
358 tableCell(sink, decimalFormatter.format(timeUnit.fromMilliseconds(aggregate.getMax())));
359 tableCell(sink, decimalFormatter.format(timeUnit.fromMilliseconds(aggregate.getTotal())));
360 sink.tableRow_();
361 }
362 sink.tableRows_();
363 sink.table_();
364 }
365
366
367
368
369
370
371
372
373
374 private void tableHeaderCell(Sink sink, String text) {
375 sink.tableHeaderCell();
376 sink.text(text);
377 sink.tableHeaderCell_();
378 }
379
380
381
382
383
384
385
386
387
388 private void tableCell(Sink sink, String text) {
389 sink.tableCell();
390 sink.text(text);
391 sink.tableCell_();
392 }
393 }