18 |
18 |
19 /* Portion Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiary(-ies). All rights reserved. */ |
19 /* Portion Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiary(-ies). All rights reserved. */ |
20 |
20 |
21 package com.nokia.ant.listener; |
21 package com.nokia.ant.listener; |
22 |
22 |
23 import java.io.*; |
23 import java.io.FileOutputStream; |
|
24 import java.io.IOException; |
|
25 import java.io.OutputStream; |
|
26 import java.io.OutputStreamWriter; |
|
27 import java.io.PrintStream; |
|
28 import java.io.Writer; |
|
29 import java.util.Hashtable; |
|
30 import java.util.Stack; |
|
31 |
|
32 import javax.xml.parsers.DocumentBuilder; |
|
33 import javax.xml.parsers.DocumentBuilderFactory; |
|
34 import javax.xml.parsers.ParserConfigurationException; |
|
35 |
|
36 import org.apache.tools.ant.BuildEvent; |
|
37 import org.apache.tools.ant.BuildException; |
|
38 import org.apache.tools.ant.BuildLogger; |
|
39 import org.apache.tools.ant.Project; |
|
40 import org.apache.tools.ant.SubBuildListener; |
|
41 import org.apache.tools.ant.Target; |
|
42 import org.apache.tools.ant.Task; |
|
43 import org.apache.tools.ant.UnknownElement; |
24 import org.apache.tools.ant.util.DOMElementWriter; |
44 import org.apache.tools.ant.util.DOMElementWriter; |
25 import org.apache.tools.ant.util.DateUtils; |
45 import org.apache.tools.ant.util.DateUtils; |
26 import javax.xml.parsers.DocumentBuilder; |
|
27 import javax.xml.parsers.DocumentBuilderFactory; |
|
28 import org.w3c.dom.Document; |
46 import org.w3c.dom.Document; |
29 import org.w3c.dom.Element; |
47 import org.w3c.dom.Element; |
30 import org.apache.tools.ant.*; |
|
31 import java.util.*; |
|
32 import javax.xml.parsers.ParserConfigurationException; |
|
33 |
48 |
34 /** |
49 /** |
35 * This is a class that represents a XML recorder. This is the listener to the |
50 * This is a class that represents a XML recorder. This is the listener to the build process. |
36 * build process. |
51 * |
37 * |
|
38 */ |
52 */ |
39 public class CoverageRecorderEntry implements BuildLogger, SubBuildListener { |
53 public class CoverageRecorderEntry implements BuildLogger, SubBuildListener { |
40 |
|
41 ////////////////////////////////////////////////////////////////////// |
|
42 // ATTRIBUTES |
|
43 |
|
44 /** XML element name for a build. */ |
54 /** XML element name for a build. */ |
45 private static final String BUILD_TAG = "build"; |
55 private static final String BUILD_TAG = "build"; |
46 /** XML element name for a target. */ |
56 /** XML element name for a target. */ |
47 private static final String TARGET_TAG = "target"; |
57 private static final String TARGET_TAG = "target"; |
48 /** XML element name for a task. */ |
58 /** XML element name for a task. */ |
51 private static final String NAME_ATTR = "name"; |
61 private static final String NAME_ATTR = "name"; |
52 /** XML attribute name for a time. */ |
62 /** XML attribute name for a time. */ |
53 private static final String TIME_ATTR = "time"; |
63 private static final String TIME_ATTR = "time"; |
54 /** XML attribute name for a file location. */ |
64 /** XML attribute name for a file location. */ |
55 private static final String LOCATION_ATTR = "location"; |
65 private static final String LOCATION_ATTR = "location"; |
56 |
66 |
57 /** DocumentBuilder to use when creating the document to start with. */ |
67 /** DocumentBuilder to use when creating the document to start with. */ |
58 private static DocumentBuilder builder = getDocumentBuilder(); |
68 private static DocumentBuilder builder = getDocumentBuilder(); |
59 |
69 |
60 private String recordTaskName; |
70 private String recordTaskName; |
61 |
71 |
62 /** The name of the file associated with this recorder entry. */ |
72 /** The name of the file associated with this recorder entry. */ |
63 private String filename; |
73 private String filename; |
64 /** The state of the recorder (recorder on or off). */ |
74 /** The state of the recorder (recorder on or off). */ |
65 private boolean record = true; |
75 private boolean record = true; |
66 /** The current verbosity level to record at. */ |
76 /** The current verbosity level to record at. */ |
67 private int loglevel = Project.MSG_INFO; |
77 private int loglevel = Project.MSG_INFO; |
68 /** The output PrintStream to record to. */ |
78 /** The output PrintStream to record to. */ |
69 private PrintStream outStream; |
79 private PrintStream outStream; |
70 /** The start time of the last know target. */ |
80 /** The start time of the last know target. */ |
71 private long targetStartTime; |
81 private long targetStartTime; |
72 /** project instance the recorder is associated with */ |
82 /** project instance the recorder is associated with */ |
73 private Project project; |
83 private Project project; |
74 |
84 |
75 /** The complete log document for this build. */ |
85 /** The complete log document for this build. */ |
77 /** Mapping for when tasks started (Task to TimedElement). */ |
87 /** Mapping for when tasks started (Task to TimedElement). */ |
78 private Hashtable<Task, TimedElement> tasks = new Hashtable<Task, TimedElement>(); |
88 private Hashtable<Task, TimedElement> tasks = new Hashtable<Task, TimedElement>(); |
79 /** Mapping for when targets started (Task to TimedElement). */ |
89 /** Mapping for when targets started (Task to TimedElement). */ |
80 private Hashtable<Target, TimedElement> targets = new Hashtable<Target, TimedElement>(); |
90 private Hashtable<Target, TimedElement> targets = new Hashtable<Target, TimedElement>(); |
81 /** |
91 /** |
82 * Mapping of threads to stacks of elements |
92 * Mapping of threads to stacks of elements (Thread to Stack of TimedElement). |
83 * (Thread to Stack of TimedElement). |
|
84 */ |
93 */ |
85 private Hashtable<Thread, Stack<TimedElement>> threadStacks = new Hashtable<Thread, Stack<TimedElement>>(); |
94 private Hashtable<Thread, Stack<TimedElement>> threadStacks = new Hashtable<Thread, Stack<TimedElement>>(); |
86 /** |
95 /** |
87 * When the build started. |
96 * When the build started. |
88 */ |
97 */ |
89 private TimedElement buildElement; |
98 private TimedElement buildElement; |
90 |
|
91 ////////////////////////////////////////////////////////////////////// |
|
92 // CONSTRUCTORS / INITIALIZERS |
|
93 |
99 |
94 /** |
100 /** |
95 * @param name The name of this recorder (used as the filename). |
101 * @param name The name of this recorder (used as the filename). |
96 */ |
102 */ |
97 public CoverageRecorderEntry(String name, String recordTaskName) { |
103 public CoverageRecorderEntry(String name, String recordTaskName) { |
98 filename = name; |
104 filename = name; |
99 this.recordTaskName = recordTaskName; |
105 this.recordTaskName = recordTaskName; |
100 startBuild(); |
106 startBuild(); |
101 } |
107 } |
102 |
108 |
103 /** |
109 /** |
104 * Returns a default DocumentBuilder instance or throws an |
110 * Returns a default DocumentBuilder instance or throws an ExceptionInInitializerError if it |
105 * ExceptionInInitializerError if it can't be created. |
111 * can't be created. |
106 * |
112 * |
107 * @return a default DocumentBuilder instance. |
113 * @return a default DocumentBuilder instance. |
108 */ |
114 */ |
109 protected static DocumentBuilder getDocumentBuilder() { |
115 protected static DocumentBuilder getDocumentBuilder() { |
110 try { |
116 try { |
111 return DocumentBuilderFactory.newInstance().newDocumentBuilder(); |
117 return DocumentBuilderFactory.newInstance().newDocumentBuilder(); |
112 } catch (ParserConfigurationException exc) { |
118 } |
|
119 catch (ParserConfigurationException exc) { |
113 throw new ExceptionInInitializerError(exc.getMessage()); |
120 throw new ExceptionInInitializerError(exc.getMessage()); |
114 } |
121 } |
115 } |
122 } |
116 |
123 |
117 /** Utility class representing the time an element started. */ |
124 /** Utility class representing the time an element started. */ |
118 protected static class TimedElement { |
125 protected static class TimedElement { |
119 /** |
126 /** |
120 * Start time in milliseconds |
127 * Start time in milliseconds (as returned by <code>System.currentTimeMillis()</code>). |
121 * (as returned by <code>System.currentTimeMillis()</code>). |
|
122 */ |
128 */ |
123 private long startTime; |
129 private long startTime; |
124 /** Element created at the start time. */ |
130 /** Element created at the start time. */ |
125 private Element element; |
131 private Element element; |
|
132 |
126 public String toString() { |
133 public String toString() { |
127 return element.getTagName() + ":" + element.getAttribute("name"); |
134 return element.getTagName() + ":" + element.getAttribute("name"); |
128 } |
135 } |
129 } |
136 } |
130 |
137 |
131 /** |
138 /** |
132 * Returns the stack of timed elements for the current thread. |
139 * Returns the stack of timed elements for the current thread. |
|
140 * |
133 * @return the stack of timed elements for the current thread |
141 * @return the stack of timed elements for the current thread |
134 */ |
142 */ |
135 protected Stack<TimedElement> getStack() { |
143 protected Stack<TimedElement> getStack() { |
136 Stack<TimedElement> threadStack = threadStacks.get(Thread.currentThread()); |
144 Stack<TimedElement> threadStack = threadStacks.get(Thread.currentThread()); |
137 if (threadStack == null) { |
145 if (threadStack == null) { |
138 threadStack = new Stack<TimedElement>(); |
146 threadStack = new Stack<TimedElement>(); |
139 threadStacks.put(Thread.currentThread(), threadStack); |
147 threadStacks.put(Thread.currentThread(), threadStack); |
140 } |
148 } |
141 /* For debugging purposes uncomment: |
149 /* |
142 org.w3c.dom.Comment s = doc.createComment("stack=" + threadStack); |
150 * For debugging purposes uncomment: org.w3c.dom.Comment s = doc.createComment("stack=" + |
143 buildElement.element.appendChild(s); |
151 * threadStack); buildElement.element.appendChild(s); |
144 */ |
152 */ |
145 return threadStack; |
153 return threadStack; |
146 } |
154 } |
147 |
|
148 ////////////////////////////////////////////////////////////////////// |
|
149 // ACCESSOR METHODS |
|
150 |
155 |
151 /** |
156 /** |
152 * @return the name of the file the output is sent to. |
157 * @return the name of the file the output is sent to. |
153 */ |
158 */ |
154 public String getFilename() { |
159 public String getFilename() { |
155 return filename; |
160 return filename; |
156 } |
161 } |
157 |
162 |
158 /** |
163 /** |
159 * Turns off or on this recorder. |
164 * Turns off or on this recorder. |
160 * |
165 * |
161 * @param state true for on, false for off, null for no change. |
166 * @param state true for on, false for off, null for no change. |
162 */ |
167 */ |
163 public void setRecordState(Boolean state) { |
168 public void setRecordState(Boolean state) { |
164 if (state != null) { |
169 if (state != null) { |
165 record = state.booleanValue(); |
170 record = state.booleanValue(); |
227 /** {@inheritDoc}. */ |
231 /** {@inheritDoc}. */ |
228 public void targetFinished(BuildEvent event) { |
232 public void targetFinished(BuildEvent event) { |
229 Target target = event.getTarget(); |
233 Target target = event.getTarget(); |
230 TimedElement targetElement = (TimedElement) targets.get(target); |
234 TimedElement targetElement = (TimedElement) targets.get(target); |
231 if (targetElement != null) { |
235 if (targetElement != null) { |
232 long totalTime |
236 long totalTime = System.currentTimeMillis() - targetElement.startTime; |
233 = System.currentTimeMillis() - targetElement.startTime; |
237 targetElement.element.setAttribute(TIME_ATTR, DateUtils.formatElapsedTime(totalTime)); |
234 targetElement.element.setAttribute(TIME_ATTR, |
|
235 DateUtils.formatElapsedTime(totalTime)); |
|
236 |
238 |
237 TimedElement parentElement = null; |
239 TimedElement parentElement = null; |
238 Stack<TimedElement> threadStack = getStack(); |
240 Stack<TimedElement> threadStack = getStack(); |
239 if (!threadStack.empty()) { |
241 if (!threadStack.empty()) { |
240 threadStack.pop(); |
242 threadStack.pop(); |
241 // if (poppedStack != targetElement) { |
243 // if (poppedStack != targetElement) { |
242 // throw new RuntimeException("Mismatch - popped element = " |
244 // throw new RuntimeException("Mismatch - popped element = " |
243 // + poppedStack |
245 // + poppedStack |
244 // + " finished target element = " |
246 // + " finished target element = " |
245 // + targetElement); |
247 // + targetElement); |
246 // } |
248 // } |
247 if (!threadStack.empty()) { |
249 if (!threadStack.empty()) { |
248 parentElement = threadStack.peek(); |
250 parentElement = threadStack.peek(); |
249 } |
251 } |
250 } |
252 } |
251 if (parentElement == null) { |
253 if (parentElement == null) { |
252 buildElement.element.appendChild(targetElement.element); |
254 buildElement.element.appendChild(targetElement.element); |
253 } else { |
255 } |
|
256 else { |
254 parentElement.element.appendChild(targetElement.element); |
257 parentElement.element.appendChild(targetElement.element); |
255 } |
258 } |
256 } |
259 } |
257 targets.remove(target); |
260 targets.remove(target); |
258 } |
261 } |
270 String name = event.getTask().getTaskName(); |
273 String name = event.getTask().getTaskName(); |
271 if (name == null) { |
274 if (name == null) { |
272 name = ""; |
275 name = ""; |
273 } |
276 } |
274 taskElement.element.setAttribute(NAME_ATTR, name); |
277 taskElement.element.setAttribute(NAME_ATTR, name); |
275 taskElement.element.setAttribute(LOCATION_ATTR, |
278 taskElement.element.setAttribute(LOCATION_ATTR, event.getTask().getLocation().toString()); |
276 event.getTask().getLocation().toString()); |
|
277 tasks.put(task, taskElement); |
279 tasks.put(task, taskElement); |
278 getStack().push(taskElement); |
280 getStack().push(taskElement); |
279 } |
281 } |
280 |
282 |
281 /** |
283 /** |
282 * @see org.apache.tools.ant.BuildListener#taskFinished(BuildEvent) |
284 * @see org.apache.tools.ant.BuildListener#taskFinished(BuildEvent) |
283 */ |
285 */ |
284 /** {@inheritDoc}. */ |
286 /** {@inheritDoc}. */ |
285 public void taskFinished(BuildEvent event) { |
287 public void taskFinished(BuildEvent event) { |
286 |
288 |
287 // if (event.getTask().getTaskName() != recordTaskName) { |
289 // if (event.getTask().getTaskName() != recordTaskName) { |
288 Task task = event.getTask(); |
290 Task task = event.getTask(); |
289 TimedElement taskElement = tasks.get(task); |
291 TimedElement taskElement = tasks.get(task); |
290 if (taskElement != null) { |
292 if (taskElement != null) { |
291 long totalTime = System.currentTimeMillis() - taskElement.startTime; |
293 long totalTime = System.currentTimeMillis() - taskElement.startTime; |
292 taskElement.element.setAttribute(TIME_ATTR, |
294 taskElement.element.setAttribute(TIME_ATTR, DateUtils.formatElapsedTime(totalTime)); |
293 DateUtils.formatElapsedTime(totalTime)); |
|
294 Target target = task.getOwningTarget(); |
295 Target target = task.getOwningTarget(); |
295 TimedElement targetElement = null; |
296 TimedElement targetElement = null; |
296 if (target != null) { |
297 if (target != null) { |
297 targetElement = (TimedElement) targets.get(target); |
298 targetElement = (TimedElement) targets.get(target); |
298 } |
299 } |
299 if (targetElement == null) { |
300 if (targetElement == null) { |
300 buildElement.element.appendChild(taskElement.element); |
301 buildElement.element.appendChild(taskElement.element); |
301 } else { |
302 } |
|
303 else { |
302 targetElement.element.appendChild(taskElement.element); |
304 targetElement.element.appendChild(taskElement.element); |
303 } |
305 } |
304 Stack<TimedElement> threadStack = getStack(); |
306 Stack<TimedElement> threadStack = getStack(); |
305 if (!threadStack.empty()) { |
307 if (!threadStack.empty()) { |
306 threadStack.pop(); |
308 threadStack.pop(); |
307 // if (poppedStack != taskElement) { |
309 // if (poppedStack != taskElement) { |
308 // throw new RuntimeException("Mismatch - popped element = " |
310 // throw new RuntimeException("Mismatch - popped element = " |
309 // + poppedStack + " finished task element = " |
311 // + poppedStack + " finished task element = " |
310 // + taskElement); |
312 // + taskElement); |
311 // } |
313 // } |
312 } |
314 } |
313 tasks.remove(task); |
315 tasks.remove(task); |
314 // } else { |
316 // } else { |
315 // throw new RuntimeException("Unknown task " + task + " not in " + tasks); |
317 // throw new RuntimeException("Unknown task " + task + " not in " + tasks); |
316 // } |
318 // } |
317 } |
319 } |
318 } |
320 } |
319 |
321 |
320 /** |
322 /** |
321 * Get the TimedElement associated with a task. |
323 * Get the TimedElement associated with a task. |
322 * |
324 * |
323 * Where the task is not found directly, search for unknown elements which |
325 * Where the task is not found directly, search for unknown elements which may be hiding the |
324 * may be hiding the real task |
326 * real task |
325 */ |
327 */ |
326 protected TimedElement getTaskElement(Task task) { |
328 protected TimedElement getTaskElement(Task task) { |
327 TimedElement element = (TimedElement) tasks.get(task); |
329 TimedElement element = (TimedElement) tasks.get(task); |
328 if (element != null) { |
330 if (element != null) { |
329 return element; |
331 return element; |
363 /** {@inheritDoc}. */ |
365 /** {@inheritDoc}. */ |
364 public void setOutputPrintStream(PrintStream output) { |
366 public void setOutputPrintStream(PrintStream output) { |
365 outStream = output; |
367 outStream = output; |
366 } |
368 } |
367 |
369 |
368 |
|
369 /** |
370 /** |
370 * @see BuildLogger#setErrorPrintStream(PrintStream) |
371 * @see BuildLogger#setErrorPrintStream(PrintStream) |
371 */ |
372 */ |
372 /** {@inheritDoc}. */ |
373 /** {@inheritDoc}. */ |
373 public void setErrorPrintStream(PrintStream err) { |
374 public void setErrorPrintStream(PrintStream err) { |
374 setOutputPrintStream(err); |
375 setOutputPrintStream(err); |
375 } |
376 } |
376 |
377 |
377 /** |
378 /** |
378 * Set the project associated with this recorder entry. |
379 * Set the project associated with this recorder entry. |
379 * |
380 * |
380 * @param project the project instance |
381 * @param project the project instance |
381 * |
382 * |
382 * @since 1.6.2 |
383 * @since 1.6.2 |
383 */ |
384 */ |
384 public void setProject(Project project) { |
385 public void setProject(Project project) { |
385 this.project = project; |
386 this.project = project; |
386 if (project != null) { |
387 if (project != null) { |
387 project.addBuildListener(this); |
388 project.addBuildListener(this); |
388 } |
389 } |
389 } |
390 } |
390 |
391 |
391 /** |
392 /** |
392 * @since 1.6.2 |
393 * @since 1.6.2 |
393 */ |
394 */ |
394 public void cleanup() { |
395 public void cleanup() { |
395 closeFile(); |
396 closeFile(); |
396 if (project != null) { |
397 if (project != null) { |
397 project.removeBuildListener(this); |
398 project.removeBuildListener(this); |
398 } |
399 } |
399 project = null; |
400 project = null; |
400 } |
401 } |
401 |
402 |
402 /** |
403 /** |
403 * Closes the file associated with this recorder. |
404 * Closes the file associated with this recorder. Used by Recorder. |
404 * Used by Recorder. |
405 * |
405 * @since 1.6.3 |
406 * @since 1.6.3 |
406 */ |
407 */ |
407 void closeFile() { |
408 void closeFile() { |
408 finishBuild(); |
409 finishBuild(); |
409 Writer out = null; |
410 Writer out = null; |
416 } |
417 } |
417 out = new OutputStreamWriter(stream, "UTF8"); |
418 out = new OutputStreamWriter(stream, "UTF8"); |
418 out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); |
419 out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); |
419 (new DOMElementWriter()).write(buildElement.element, out, 0, "\t"); |
420 (new DOMElementWriter()).write(buildElement.element, out, 0, "\t"); |
420 out.flush(); |
421 out.flush(); |
421 } catch (IOException exc) { |
422 } |
|
423 catch (IOException exc) { |
422 throw new BuildException("Unable to write log file " + exc.getMessage(), exc); |
424 throw new BuildException("Unable to write log file " + exc.getMessage(), exc); |
423 } finally { |
425 } |
|
426 finally { |
424 if (out != null) { |
427 if (out != null) { |
425 try { |
428 try { |
426 out.close(); |
429 out.close(); |
427 } catch (IOException e) { |
430 } |
|
431 catch (IOException e) { |
428 e = null; // ignore |
432 e = null; // ignore |
429 } |
433 } |
430 } |
434 } |
431 } |
435 } |
432 buildElement = null; |
436 buildElement = null; |
433 } |
437 } |
434 |
438 |
435 void startBuild() { |
439 void startBuild() { |
436 buildElement = new TimedElement(); |
440 buildElement = new TimedElement(); |
437 buildElement.startTime = System.currentTimeMillis(); |
441 buildElement.startTime = System.currentTimeMillis(); |
438 buildElement.element = doc.createElement(BUILD_TAG); |
442 buildElement.element = doc.createElement(BUILD_TAG); |
439 } |
443 } |
440 |
444 |
441 void finishBuild() { |
445 void finishBuild() { |
442 long totalTime = System.currentTimeMillis() - buildElement.startTime; |
446 long totalTime = System.currentTimeMillis() - buildElement.startTime; |
443 buildElement.element.setAttribute(TIME_ATTR, |
447 buildElement.element.setAttribute(TIME_ATTR, DateUtils.formatElapsedTime(totalTime)); |
444 DateUtils.formatElapsedTime(totalTime)); |
|
445 } |
448 } |
446 |
449 |
447 @Override |
450 @Override |
448 public void setEmacsMode(boolean emacsMode) { |
451 public void setEmacsMode(boolean emacsMode) { |
449 // Not needed. |
452 // Not needed. |