• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright (c) 2000, 2009 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package org.eclipse.test.internal.performance.results.db;
12 
13 import java.io.BufferedInputStream;
14 import java.io.BufferedOutputStream;
15 import java.io.DataInputStream;
16 import java.io.DataOutputStream;
17 import java.io.File;
18 import java.io.FileInputStream;
19 import java.io.FileNotFoundException;
20 import java.io.FileOutputStream;
21 import java.io.IOException;
22 import java.io.PrintStream;
23 import java.util.*;
24 
25 import org.eclipse.core.runtime.IProgressMonitor;
26 import org.eclipse.core.runtime.OperationCanceledException;
27 import org.eclipse.core.runtime.SubMonitor;
28 import org.eclipse.test.internal.performance.results.utils.Util;
29 
30 
31 /**
32  * Root class to handle performance results.
33  *
34  * Usually performance results are built for a current build vs. a baseline build.
35  *
36  * This class allow to read all data from releng performance database for given
37  * configurations and scenario pattern.
38  *
39  * Then it provides easy and speedy access to all stored results.
40  */
41 public class PerformanceResults extends AbstractResults {
42 
43 	String[] allBuildNames = null;
44 	Map allScenarios;
45 	String lastBuildName; // Name of the last used build
46 	String baselineName; // Name of the baseline build used for comparison
47 	String baselinePrefix;
48 	private String scenarioPattern = "%"; //$NON-NLS-1$
49 	private String[] components;
50 	String[] configNames, sortedConfigNames;
51 	String[] configDescriptions, sortedConfigDescriptions;
52 	private String configPattern;
53 
54 	boolean dbRequired;
55 	boolean needToUpdateLocalFile;
56 
57 	/*
58 	 * Local class helping to guess remaining time while reading results from DB
59 	 */
60 	class RemainingTimeGuess {
61 		int all, count;
62 		long start;
63 		double remaining;
RemainingTimeGuess(int all, long start)64 		RemainingTimeGuess(int all, long start) {
65 			this.all = all;
66 			this.start = start;
67 		}
display()68 		String display() {
69 			StringBuffer buffer = new StringBuffer(" [elapsed: "); //$NON-NLS-1$
70 			long elapsed = getElapsed();
71 			buffer.append(Util.timeChrono(elapsed));
72 			if (this.count > 0) {
73 				buffer.append(" | left: "); //$NON-NLS-1$
74 				long remainingTime = getRemainingTime(elapsed);
75 				buffer.append(Util.timeChrono(remainingTime));
76 				buffer.append(" | end: "); //$NON-NLS-1$
77 				buffer.append(Util.timeEnd(remainingTime));
78 			}
79 			buffer.append(']');
80 			return buffer.toString();
81 		}
getRemainingTime(long elapsed)82 		private long getRemainingTime(long elapsed) {
83 			return (long) ((((double)elapsed) / this.count) * (this.all - this.count));
84 	    }
getElapsed()85 		private long getElapsed() {
86 			return System.currentTimeMillis() - this.start;
87 	    }
88 	}
89 
90 
91 	// Failure threshold
92 	public static final int DEFAULT_FAILURE_THRESHOLD = 10;
93 	int failure_threshold = DEFAULT_FAILURE_THRESHOLD;
94 
PerformanceResults(PrintStream stream)95 public PerformanceResults(PrintStream stream) {
96 	super(null, null);
97 	this.printStream = stream;
98 	this.dbRequired = false;
99 	setDefaults();
100 }
101 
PerformanceResults(String name, String baseline, String baselinePrefix, PrintStream stream)102 public PerformanceResults(String name, String baseline, String baselinePrefix, PrintStream stream) {
103 	super(null, name);
104 	this.baselineName = baseline;
105 	this.baselinePrefix = baselinePrefix;
106 	this.printStream = stream;
107 	this.dbRequired = true;
108 	setDefaults();
109 }
110 
111 /**
112  * Returns the list of all builds currently read.
113  *
114  * @return The names list of all currently known builds
115  */
getAllBuildNames()116 public String[] getAllBuildNames() {
117 	if (this.allBuildNames == null) {
118 		setAllBuildNames();
119 	}
120 	return this.allBuildNames;
121 }
122 
123 /**
124  * Returns the name of the baseline used for extracted results
125  *
126  * @return The build name of the baseline of <code>null</code>
127  * 	if no specific baseline is used for the extracted results.
128  */
getBaselineName()129 public String getBaselineName() {
130 	return this.baselineName;
131 }
132 
133 /*
134  * Get the baseline prefix (computed from #baselineName).
135  */
getBaselinePrefix()136 String getBaselinePrefix() {
137 	return this.baselinePrefix;
138 }
139 
140 /*
141  * Get the build date (see #getBuildDate(String, String)).
142  */
getBuildDate()143 public String getBuildDate() {
144 	String buildName = getName();
145 	if (buildName == null) return ""; //$NON-NLS-1$
146 	return Util.getBuildDate(getName(), getBaselinePrefix());
147 }
148 
149 /**
150  * Return the list of components concerned by performance results.
151  *
152  * @return The list of the components
153  */
getComponents()154 public String[] getComponents() {
155 	return this.components;
156 }
157 
158 /**
159  * Get the scenarios of a given component.
160  *
161  * @param componentName The component name. Should not be <code>null</code>
162  * @return A list of {@link ScenarioResults scenario results}
163  */
getComponentScenarios(String componentName)164 public List getComponentScenarios(String componentName) {
165 	ComponentResults componentResults = (ComponentResults) getResults(componentName);
166 	if (componentResults == null) return null;
167 	return Collections.unmodifiableList(componentResults.children);
168 }
169 
170 /**
171  * Get the scenarios which have a summary for a given component.
172  *
173  * @param componentName The component name
174  * @param config Configuration name
175  * @return A list of {@link ScenarioResults scenario results} which have a summary
176  */
getComponentSummaryScenarios(String componentName, String config)177 public List getComponentSummaryScenarios(String componentName, String config) {
178 	if (componentName == null) {
179 		int size = size();
180 		List scenarios = new ArrayList();
181 		for (int i=0; i< size; i++) {
182 			ComponentResults componentResults = (ComponentResults) this.children.get(i);
183 			scenarios.addAll(componentResults.getSummaryScenarios(true, config));
184 		}
185 		return scenarios;
186 	}
187 	ComponentResults componentResults = (ComponentResults) getResults(componentName);
188 	return componentResults.getSummaryScenarios(false, config);
189 }
190 
191 /**
192  * Return the configuration boxes considered for this performance results
193  * sorted or not depending on the given flag.
194  *
195  * @param sort Indicates whether the list must be sorted or not.
196  * 	The order is defined by the configuration names, not by the box names
197  * @return The list of configuration boxes sorted by configuration names
198  */
getConfigBoxes(boolean sort)199 public String[] getConfigBoxes(boolean sort) {
200 	return sort ? this.sortedConfigDescriptions : this.configDescriptions;
201 }
202 
203 /**
204  * Return the configuration names considered for this performance results
205  * sorted or not depending on the given flag.
206  *
207  * @param sort Indicates whether the list must be sorted or not
208  * @return The list of configuration names
209  */
getConfigNames(boolean sort)210 public String[] getConfigNames(boolean sort) {
211 	return sort ?this.sortedConfigNames : this.configNames;
212 }
213 
214 /*
215  * Compute a SQL pattern from all stored configuration names.
216  * For example 'eclipseperflnx1', 'eclipseperflnx2' and 'eclipseperflnx3'
217  * will return 'eclipseperflnx_'.
218  */
getConfigurationsPattern()219 String getConfigurationsPattern() {
220 	if (this.configPattern == null) {
221 		int length = this.sortedConfigNames == null ? 0 : this.sortedConfigNames.length;
222 		if (length == 0) return null;
223 		this.configPattern = this.sortedConfigNames[0];
224 		int refLength = this.configPattern.length();
225 		for (int i=1; i<length; i++) {
226 			String config = this.sortedConfigNames[i];
227 			StringBuffer newConfig = null;
228 			if (refLength != config.length()) return null; // strings have not the same length => cannot find a pattern
229 			for (int j=0; j<refLength; j++) {
230 				char c = this.configPattern.charAt(j);
231 				if (config.charAt(j) != c) {
232 					if (newConfig == null) {
233 						newConfig = new StringBuffer(refLength);
234 						if (j == 0) return null; // first char is already different => cannot find a pattern
235 						newConfig.append(this.configPattern.substring(0, j));
236 					}
237 					newConfig.append('_');
238 				} else if (newConfig != null) {
239 					newConfig.append(c);
240 				}
241 			}
242 			if (newConfig != null) {
243 				this.configPattern = newConfig.toString();
244 			}
245 		}
246 	}
247 	return this.configPattern;
248 }
249 
250 /**
251  * Return the name of the last build name except baselines.
252  *
253  * @return the name of the last build
254  */
getLastBuildName()255 public String getLastBuildName() {
256 	return getLastBuildName(1/*all except baselines*/);
257 }
258 /**
259  * Return the name of the last build name
260  *
261  * @param kind Decide what kind of build is taken into account
262  * 	0: all kind of build
263  * 	1: all except baseline builds
264  * 	2: all except baseline and nightly builds
265  * 	3: only integration builds
266  * @return the name of the last build of the selected kind
267  */
getLastBuildName(int kind)268 public String getLastBuildName(int kind) {
269 	if (this.name == null) {
270 		getAllBuildNames(); // init build names if necessary
271 		int idx = this.allBuildNames.length-1;
272 		this.name = this.allBuildNames[idx];
273 		if (kind > 0) {
274 			loop: while (idx-- >= 0) {
275 				switch (this.name.charAt(0)) {
276 					case 'N':
277 						if (kind < 2) break loop;
278 						break;
279 					case 'M':
280 						if (kind < 3) break loop;
281 						break;
282 					case 'I':
283 						if (kind < 4) break loop;
284 						break;
285 				}
286 				this.name = this.allBuildNames[idx];
287 			}
288 		}
289 	}
290 	return this.name;
291 }
292 
getName()293 public String getName() {
294 	if (this.name == null) {
295 		setAllBuildNames();
296 	}
297 	return this.name;
298 }
299 
300 /*
301  * (non-Javadoc)
302  * @see org.eclipse.test.internal.performance.results.AbstractResults#getPerformance()
303  */
getPerformance()304 PerformanceResults getPerformance() {
305 	return this;
306 }
307 
308 /**
309  * Get the results of a given scenario.
310  *
311  * @param scenarioName The scenario name
312  * @return The {@link ScenarioResults scenario results}
313  */
getScenarioResults(String scenarioName)314 public ScenarioResults getScenarioResults(String scenarioName) {
315 	ComponentResults componentResults = (ComponentResults) getResults(DB_Results.getComponentNameFromScenario(scenarioName));
316 	return componentResults == null ? null : (ScenarioResults) componentResults.getResults(scenarioName);
317 }
318 
319 /*
320  * Init configurations from performance results database.
321  */
initConfigs()322 private void initConfigs() {
323 	// create config names
324 	this.configNames = DB_Results.getConfigs();
325 	this.configDescriptions = DB_Results.getConfigDescriptions();
326 	int length = this.configNames.length;
327 	this.sortedConfigNames = new String[length];
328 	for (int i = 0; i < length; i++) {
329 	    this.sortedConfigNames[i] = this.configNames[i];
330 	}
331 
332 	// Sort the config names
333 	Arrays.sort(this.sortedConfigNames);
334 	this.sortedConfigDescriptions = new String[length];
335 	for (int i=0; i<length; i++) {
336 		for (int j=0; j<length; j++) {
337 			if (this.sortedConfigNames[i] == this.configNames[j]) { // == is intentional!
338 				this.sortedConfigDescriptions[i] = this.configDescriptions[j];
339 				break;
340 			}
341 		}
342 	}
343 }
344 
345 /*
346  * Read or update data for a build from a directory supposed to have local files.
347  */
read(boolean local, String buildName, String[][] configs, boolean force, File dataDir, String taskName, SubMonitor subMonitor)348 private String[] read(boolean local, String buildName, String[][] configs, boolean force, File dataDir, String taskName, SubMonitor subMonitor) {
349 	if (local && dataDir == null) {
350 		throw new IllegalArgumentException("Must specify a directory to read local files!"); //$NON-NLS-1$
351 	}
352 	subMonitor.setWorkRemaining(100);
353 
354 	// Update info
355 	long start = System.currentTimeMillis();
356 	int allScenariosSize;
357 	if (DB_Results.DB_CONNECTION) {
358 		try {
359 			// Read all scenarios
360 			allScenariosSize = readScenarios(buildName, subMonitor.newChild(10)) ;
361 			if (allScenariosSize < 0) {
362 				return null;
363 			}
364 
365 			// Read all builds
366 			DB_Results.queryAllVariations(getConfigurationsPattern());
367 
368 			// Refresh configs
369 			if (configs == null) {
370 				initConfigs();
371 			} else {
372 				setConfigInfo(configs);
373 			}
374 		} catch (OperationCanceledException e) {
375 			return null;
376 		}
377 	} else {
378 		if (this.allScenarios == null) return null;
379 		allScenariosSize = this.allScenarios.size();
380 		if (configs != null) {
381 			setConfigInfo(configs);
382 		}
383 	}
384 
385 	// Create corresponding children
386 	int componentsLength = this.components.length;
387 	subMonitor.setWorkRemaining(componentsLength);
388 	RemainingTimeGuess timeGuess = null;
389 	for (int i=0; i<componentsLength; i++) {
390 		String componentName = this.components[i];
391 		List scenarios = this.allScenarios == null ? null : (List) this.allScenarios.get(componentName);
392 
393 		// Manage monitor
394 		int percentage = (int) ((((double)(i+1)) / (componentsLength+1)) * 100);
395 		StringBuffer tnBuffer= taskName==null ? new StringBuffer() : new StringBuffer(taskName);
396 		tnBuffer.append(" ("); //$NON-NLS-1$
397 		if (buildName != null) {
398 			tnBuffer.append(buildName).append(": "); //$NON-NLS-1$
399 		}
400 		tnBuffer.append(percentage).append("%)"); //$NON-NLS-1$
401 		subMonitor.setTaskName(tnBuffer.toString());
402 		StringBuffer subTaskBuffer = new StringBuffer("Component "); //$NON-NLS-1$
403 		subTaskBuffer.append(componentName);
404 		subTaskBuffer.append("..."); //$NON-NLS-1$
405 		subMonitor.subTask(subTaskBuffer.toString());
406 
407 		// Get component results
408 		if (scenarios == null && !local) continue;
409 		ComponentResults componentResults;
410 		if (local || (buildName == null && force)) {
411 			componentResults = new ComponentResults(this, componentName);
412 			addChild(componentResults, true);
413 		} else {
414 			componentResults = (ComponentResults) getResults(componentName);
415 			if (componentResults == null) {
416 				componentResults = new ComponentResults(this, componentName);
417 				addChild(componentResults, true);
418 			}
419 		}
420 
421 		// Read the component results
422 		if (local) {
423 			try {
424 				componentResults.readLocalFile(dataDir, scenarios);
425 			}
426 			catch (FileNotFoundException ex) {
427 				return null;
428 			}
429 			subMonitor.worked(1);
430 		} else {
431 			if (timeGuess == null) {
432 				timeGuess = new RemainingTimeGuess(1+componentsLength+allScenariosSize, start);
433 			}
434 			componentResults.updateBuild(buildName, scenarios, force, dataDir, subMonitor.newChild(1), timeGuess);
435 		}
436 		if (subMonitor.isCanceled()) return null;
437 	}
438 
439 	// Update names
440 	setAllBuildNames();
441 	writeData(dataDir);
442 
443 	// Print time
444 	printGlobalTime(start);
445 
446 	return this.allBuildNames;
447 }
448 
449 /**
450  * Read all data from performance database for the given configurations
451  * and scenario pattern.
452  *
453  * This method is typically called when generated performance results
454  * from a non-UI application.
455  *
456  * @param buildName The name of the build
457  * @param configs All configurations to extract results. If <code>null</code>,
458  * 	then all known configurations ({@link DB_Results#getConfigs()})  are read.
459  * @param pattern The pattern of the concerned scenarios
460  * @param dataDir The directory where data will be read/stored locally.
461  * 	If <code>null</code>, then database will be read instead and no storage
462  * 	will be performed
463  * @param threshold The failure percentage threshold over which a build result
464  * 	value compared to the baseline is considered as failing.
465  * @param monitor The progress monitor
466  *
467  * @return All known build names
468  */
readAll(String buildName, String[][] configs, String pattern, File dataDir, int threshold, IProgressMonitor monitor)469 public String[] readAll(String buildName, String[][] configs, String pattern, File dataDir, int threshold, IProgressMonitor monitor) {
470 
471 	// Init
472 	this.scenarioPattern = pattern == null ? "%" : pattern; //$NON-NLS-1$
473 	this.failure_threshold = threshold;
474 	SubMonitor subMonitor = SubMonitor.convert(monitor, 1000);
475 
476 	// Set default names
477 	setDefaults();
478 
479 	// Read local data files first
480 	reset(dataDir);
481 	String[] names = read(true, null, configs, true, dataDir, null, subMonitor.newChild(100));
482 	if (names==null) {
483 		// if one local files is missing then force a full DB read!
484 		// TODO moderate this to force the DB read only for the missing file...
485 		return read(false, null, configs, true, dataDir, null, subMonitor.newChild(900));
486 	}
487 
488 	// Search build name in read data
489 	boolean buildMissing = true;
490 	if (buildName != null) {
491 		this.name = buildName;
492 		buildMissing = Arrays.binarySearch(names, buildName, Util.BUILD_DATE_COMPARATOR) < 0;
493 	}
494 
495 	// Look for missing builds
496 	if (buildMissing) {
497 		String[] builds = DB_Results.getBuilds();
498 		Arrays.sort(builds, Util.BUILD_DATE_COMPARATOR);
499 		for (int i=builds.length-1; i>=0; i--) {
500 			if (Arrays.binarySearch(names, builds[i], Util.BUILD_DATE_COMPARATOR) >= 0) {
501 				break;
502 			}
503 			read(false, builds[i], configs, true, dataDir, null, subMonitor.newChild(900));
504 		}
505 	}
506 	return this.allBuildNames;
507 }
508 
509 /**
510  * Read all data from performance database for the given configurations
511  * and scenario pattern.
512  *
513  * Note that calling this method flush all previous read data.
514  *
515  * @param dataDir The directory where local files are located
516  * @param monitor  The progress monitor
517  */
readLocal(File dataDir, IProgressMonitor monitor)518 public void readLocal(File dataDir, IProgressMonitor monitor) {
519 
520 	// Print title
521 	String taskName = "Read local performance results"; //$NON-NLS-1$
522 	println(taskName);
523 
524 	// Create monitor
525 	SubMonitor subMonitor = SubMonitor.convert(monitor, 1000);
526 	subMonitor.setTaskName(taskName);
527 
528 	// Read
529 	reset(dataDir);
530 	read(true, null, null, true, dataDir, taskName, subMonitor);
531 }
532 
readLocalFile(File dir)533 void readLocalFile(File dir) {
534 	if (!dir.exists()) return;
535 	File dataFile = new File(dir, "performances.dat");	//$NON-NLS-1$
536 	if (!dataFile.exists()) return;
537 	DataInputStream stream = null;
538 	try {
539 		// Read local file info
540 		stream = new DataInputStream(new BufferedInputStream(new FileInputStream(dataFile)));
541 
542 		// Read build info
543 		String str = stream.readUTF();
544 		this.needToUpdateLocalFile = this.name == null || Util.getBuildDate(this.name).compareTo(Util.getBuildDate(str)) > 0;
545 		if (this.name != null && Util.getBuildDate(this.name).compareTo(Util.getBuildDate(str)) >= 0) {
546 			return;
547 		}
548 		println(" - read performance results local files info: "); //$NON-NLS-1$
549 		println("		+ name : "+str);
550 		this.name = str == ""  ? null : str;
551 		str = stream.readUTF();
552 		println("		+ baseline : "+str);
553 		if (this.baselineName == null) {
554 			this.baselineName = str == "" ? null : str;
555 		}
556 		str = stream.readUTF();
557 		println("		+ baseline prefix: "+str);
558 		this.baselinePrefix = str == "" ? null : str;
559 
560 		// Write configs info
561 		int length = stream.readInt();
562 		println("		+ "+length+" configs");
563 		this.configNames = new String[length];
564 		this.sortedConfigNames = new String[length];
565 		this.configDescriptions = new String[length];
566 		this.sortedConfigDescriptions = new String[length];
567 		for (int i = 0; i < length; i++) {
568 			this.configNames[i] = stream.readUTF();
569 			this.sortedConfigNames[i] = this.configNames[i];
570 			this.configDescriptions[i] = stream.readUTF();
571 			this.sortedConfigDescriptions[i] = this.configDescriptions[i];
572 		}
573 		DB_Results.setConfigs(this.configNames);
574 		DB_Results.setConfigDescriptions(this.configDescriptions);
575 
576 		// Write builds info
577 		length = stream.readInt();
578 		println("		+ "+length+" builds");
579 		this.allBuildNames = new String[length];
580 		for (int i = 0; i < length; i++) {
581 			this.allBuildNames[i] = stream.readUTF();
582 		}
583 
584 		// Write scenarios info
585 		length = stream.readInt();
586 		println("		+ "+length+" components");
587 		this.components = new String[length];
588 		this.allScenarios = new HashMap();
589 		for (int i = 0; i < length; i++) {
590 			this.components[i] = stream.readUTF();
591 			int size = stream.readInt();
592 			List scenarios = new ArrayList(size);
593 			for (int j=0; j<size; j++) {
594 				scenarios.add(new ScenarioResults(stream.readInt(), stream.readUTF(), stream.readUTF()));
595 			}
596 			this.allScenarios.put(this.components[i], scenarios);
597 		}
598 		println("	=> read from file "+dataFile); //$NON-NLS-1$
599 	} catch (IOException ioe) {
600 		println("	!!! "+dataFile+" should be deleted as it contained invalid data !!!"); //$NON-NLS-1$ //$NON-NLS-2$
601 	} finally {
602 		try {
603 	        stream.close();
604         } catch (IOException e) {
605 	        // nothing else to do!
606         }
607 	}
608 }
609 
readScenarios(String buildName, SubMonitor subMonitor)610 private int readScenarios(String buildName, SubMonitor subMonitor) throws OperationCanceledException {
611 	subMonitor.setWorkRemaining(10);
612 	long start = System.currentTimeMillis();
613 	String titleSuffix;
614 	if (buildName == null) {
615 		titleSuffix = "all database scenarios..."; //$NON-NLS-1$
616 	} else {
617 		titleSuffix = "all database scenarios for "+buildName+" build..."; //$NON-NLS-1$ //$NON-NLS-2$
618 	}
619 	print("	+ get "+titleSuffix); //$NON-NLS-1$
620 	subMonitor.subTask("Get "+titleSuffix); //$NON-NLS-1$
621 	this.allScenarios = DB_Results.queryAllScenarios(this.scenarioPattern, buildName);
622 	if (this.allScenarios == null) return -1;
623 	int allScenariosSize = 0;
624 	List componentsSet = new ArrayList(this.allScenarios.keySet());
625 	Collections.sort(componentsSet);
626 	int componentsSize = componentsSet.size();
627 	componentsSet.toArray(this.components = new String[componentsSize]);
628 	for (int i=0; i<componentsSize; i++) {
629 		String componentName = this.components[i];
630 		List scenarios = (List) this.allScenarios.get(componentName);
631 		allScenariosSize += scenarios.size();
632 	}
633 	println(" -> "+allScenariosSize+" found in "+(System.currentTimeMillis()-start)+"ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
634 	subMonitor.worked(10);
635 	if (subMonitor.isCanceled()) throw new OperationCanceledException();
636 	return allScenariosSize;
637 }
638 
reset(File dataDir)639 void reset(File dataDir) {
640 	this.allBuildNames = null;
641 	this.children = new ArrayList();
642 //	this.name = null;
643 	this.components = null;
644 	this.allScenarios = null;
645 	readLocalFile(dataDir);
646 }
647 
setAllBuildNames()648 private void setAllBuildNames() {
649 	SortedSet builds = new TreeSet(Util.BUILD_DATE_COMPARATOR);
650 	int size = size();
651 	if (size == 0) return;
652 	for (int i=0; i<size; i++) {
653 		ComponentResults componentResults = (ComponentResults) this.children.get(i);
654 		Set names = componentResults.getAllBuildNames();
655 		builds.addAll(names);
656 	}
657 	int buildsSize = builds.size();
658 	this.allBuildNames = new String[buildsSize];
659 	if (buildsSize > 0) {
660 		int n = 0;
661 		Iterator buildNames = builds.iterator();
662 		while (buildNames.hasNext()) {
663 			String buildName = (String) buildNames.next();
664 			if (this.lastBuildName == null || Util.getBuildDate(buildName).compareTo(Util.getBuildDate(this.lastBuildName)) <= 0) {
665 				this.allBuildNames[n++] = buildName;
666 			}
667 		}
668 		if (n < buildsSize) {
669 			System.arraycopy(this.allBuildNames, 0, this.allBuildNames = new String[n], 0, n);
670 		}
671 		int idx = n-1;
672 		String lastBuild = this.allBuildNames[idx--];
673 		while (lastBuild.startsWith(DB_Results.getDbBaselinePrefix())) {
674 			lastBuild = this.allBuildNames[idx--];
675 		}
676 		this.needToUpdateLocalFile = this.name == null || Util.getBuildDate(lastBuild).compareTo(Util.getBuildDate(this.name)) > 0;
677 		this.name = lastBuild;
678 		if (this.baselineName != null) {
679 			String lastBuildDate = Util.getBuildDate(lastBuild);
680 			if (Util.getBuildDate(this.baselineName).compareTo(lastBuildDate) > 0) {
681 				this.baselineName = DB_Results.getLastBaselineBuild(lastBuildDate);
682 			}
683 		}
684 	}
685 }
686 
setConfigInfo(String[][] configs)687 private void setConfigInfo(String[][] configs) {
688 	if (configs == null) return;
689 
690 	// Store config information
691 	int length = configs.length;
692 	this.configNames = new String[length];
693 	this.sortedConfigNames = new String[length];
694 	this.configDescriptions = new String[length];
695 	for (int i=0; i<length; i++) {
696 		this.configNames[i] = this.sortedConfigNames[i] = configs[i][0];
697 		this.configDescriptions[i] = configs[i][1];
698 	}
699 
700 	// Sort the config names
701 	Arrays.sort(this.sortedConfigNames);
702 	length = this.sortedConfigNames.length;
703 	this.sortedConfigDescriptions = new String[length];
704 	for (int i=0; i<length; i++) {
705 		for (int j=0; j<length; j++) {
706 			if (this.sortedConfigNames[i] == this.configNames[j]) { // == is intentional!
707 				this.sortedConfigDescriptions[i] = this.configDescriptions[j];
708 				break;
709 			}
710 		}
711 	}
712 }
713 
714 
715 /**
716  * Set the name of the baseline used for extracted results
717  *
718  * @param buildName The name of the baseline build
719  */
setBaselineName(String buildName)720 public void setBaselineName(String buildName) {
721 	this.baselineName = buildName;
722 }
723 
setDefaults()724 private void setDefaults() {
725 
726 	// Set builds if none
727 	if (size() == 0 && DB_Results.DB_CONNECTION) {
728 		this.allBuildNames = DB_Results.getBuilds();
729 		this.components = DB_Results.getComponents();
730 		initConfigs();
731 	}
732 
733 	// Set name if null
734 	if (this.name == null) {
735 		setAllBuildNames();
736 		if (this.name == null) { // does not know any build
737 			this.name = DB_Results.getLastCurrentBuild();
738 			if (this.dbRequired) {
739 				if (this.name == null) {
740 					throw new RuntimeException("Cannot find any current build!"); //$NON-NLS-1$
741 				}
742 				this.allBuildNames = DB_Results.getBuilds();
743 				this.components = DB_Results.getComponents();
744 				initConfigs();
745 			}
746 			if (this.printStream != null) {
747 				this.printStream.println("	+ no build specified => use last one: "+this.name); //$NON-NLS-1$
748 			}
749 		}
750 	}
751 
752 	// Init baseline name if not set
753 	if (this.baselineName == null && getName() != null) {
754 		String buildDate = Util.getBuildDate(getName());
755 		this.baselineName = DB_Results.getLastBaselineBuild(buildDate);
756 		if (this.baselineName == null && this.dbRequired) {
757 			throw new RuntimeException("Cannot find any baseline to refer!"); //$NON-NLS-1$
758 		}
759 		if (this.printStream != null) {
760 			this.printStream.println("	+ no baseline specified => use last one: "+this.baselineName); //$NON-NLS-1$
761 		}
762 	}
763 
764 	// Init baseline prefix if not set
765 	if (this.baselinePrefix == null && this.baselineName != null) {
766 		// Assume that baseline name format is *always* x.y_yyyyMMddhhmm_yyyyMMddhhmm
767 		int index = this.baselineName.lastIndexOf('_');
768 		if (index > 0) {
769 			this.baselinePrefix = this.baselineName.substring(0, index);
770 		} else {
771 			this.baselinePrefix = DB_Results.getDbBaselinePrefix();
772 		}
773 	}
774 
775 	// Set scenario pattern default
776 	if (this.scenarioPattern == null) {
777 		this.scenarioPattern = "%"; //$NON-NLS-1$
778 	}
779 
780 	// Flush print stream
781 	if (this.printStream != null) {
782 		this.printStream.println();
783 		this.printStream.flush();
784 	}
785 }
786 
setLastBuildName(String lastBuildName)787 public void setLastBuildName(String lastBuildName) {
788 	this.lastBuildName = lastBuildName;
789 //	if (lastBuildName == null) {
790 //		int idx = this.allBuildNames.length-1;
791 //		String lastBuild = this.allBuildNames[idx--];
792 //		while (this.name.startsWith(DB_Results.getDbBaselinePrefix())) {
793 //			lastBuild = this.allBuildNames[idx--];
794 //		}
795 //		this.name = lastBuild;
796 //	} else {
797 //		this.name = lastBuildName;
798 //	}
799 }
800 
801 /**
802  * Update a given build information with database contents.
803  *
804  * @param builds The builds to read new data
805  * @param force Force the update from the database, even if the build is
806  * 	already known.
807  * @param dataDir The directory where data should be stored locally if necessary.
808  * 	If <code>null</code>, then information changes won't be persisted.
809  * @param monitor The progress monitor
810  * @return All known builds
811  */
updateBuilds(String[] builds, boolean force, File dataDir, IProgressMonitor monitor)812 public String[] updateBuilds(String[] builds, boolean force, File dataDir, IProgressMonitor monitor) {
813 
814 	// Print title
815 	StringBuffer buffer = new StringBuffer("Update data for "); //$NON-NLS-1$
816 	int length = builds == null ? 0 : builds.length;
817 	switch (length) {
818 		case 0:
819 			buffer.append("all builds"); //$NON-NLS-1$
820 			reset(dataDir);
821 			break;
822 		case 1:
823 			buffer.append("one build"); //$NON-NLS-1$
824 			break;
825 		default:
826 			buffer.append("several builds"); //$NON-NLS-1$
827 			break;
828 	}
829 	String taskName = buffer.toString();
830 	println(buffer);
831 
832 	// Create sub-monitor
833 	SubMonitor subMonitor = SubMonitor.convert(monitor, 1000*length);
834 	subMonitor.setTaskName(taskName);
835 
836 	// Read
837 	for (int i=0; i<length;  i++) {
838 		read(false, builds[i], null, force, dataDir, taskName, subMonitor.newChild(1000));
839 	}
840 
841 	// Return new builds list
842 	return this.allBuildNames;
843 }
844 
845 /**
846  * Update a given build information with database contents.
847  *
848  * @param buildName The build name to read new data
849  * @param force Force the update from the database, even if the build is
850  * 	already known.
851  * @param dataDir The directory where data should be stored locally if necessary.
852  * 	If <code>null</code>, then information changes won't be persisted.
853  * @param monitor The progress monitor
854  * @return All known builds
855  */
updateBuild(String buildName, boolean force, File dataDir, IProgressMonitor monitor)856 public String[] updateBuild(String buildName, boolean force, File dataDir, IProgressMonitor monitor) {
857 
858 	// Print title
859 	StringBuffer buffer = new StringBuffer("Update data for "); //$NON-NLS-1$
860 	if (buildName == null) {
861 		buffer.append("all builds"); //$NON-NLS-1$
862 		reset(dataDir);
863 	} else {
864 		buffer.append("one build"); //$NON-NLS-1$
865 	}
866 	String taskName = buffer.toString();
867 	println(buffer);
868 
869 	// Create sub-monitor
870 	SubMonitor subMonitor = SubMonitor.convert(monitor, 1000);
871 	subMonitor.setTaskName(taskName);
872 
873 	// Read
874 	read(false, buildName, null, force, dataDir, taskName, subMonitor);
875 
876 	// Refresh name
877 	if (buildName != null && !buildName.startsWith(DB_Results.getDbBaselinePrefix())) {
878 		this.name = buildName;
879 	}
880 
881 	// Return new list all build names
882 	return this.allBuildNames;
883 }
884 
885 /*
886  * Write general information.
887  */
writeData(File dir)888 void writeData(File dir) {
889 	if (!DB_Results.DB_CONNECTION) {
890 		// Only write new local file if there's a database connection
891 		// otherwise contents may not be complete...
892 		return;
893 	}
894 	if (dir ==null || (!dir.exists() && !dir.mkdirs())) {
895 		System.err.println("can't create directory " + dir); //$NON-NLS-1$
896 		return;
897 	}
898 	File dataFile = new File(dir, "performances.dat"); //$NON-NLS-1$
899 	if (dataFile.exists()) {
900 		if (!this.needToUpdateLocalFile) {
901 			return;
902 		}
903 		dataFile.delete();
904 	}
905 	try {
906 		DataOutputStream stream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(dataFile)));
907 
908 		// Write build info
909 		stream.writeUTF(this.name == null ? DB_Results.getLastCurrentBuild() : this.name);
910 		stream.writeUTF(this.baselineName == null ? DB_Results.getLastBaselineBuild(null) : this.baselineName);
911 		stream.writeUTF(this.baselinePrefix == null ? "" : this.baselinePrefix);
912 
913 		// Write configs info
914 		int length = this.sortedConfigNames.length;
915 		stream.writeInt(length);
916 		for (int i = 0; i < length; i++) {
917 			stream.writeUTF(this.sortedConfigNames[i]);
918 			stream.writeUTF(this.sortedConfigDescriptions[i]);
919 		}
920 
921 		// Write builds info
922 		String[] builds = this.allBuildNames == null ? DB_Results.getBuilds() : this.allBuildNames;
923 		length = builds.length;
924 		stream.writeInt(length);
925 		for (int i = 0; i < length; i++) {
926 			stream.writeUTF(builds[i]);
927 		}
928 
929 		// Write scenarios info
930 		length = this.components.length;
931 		stream.writeInt(length);
932 		for (int i = 0; i < length; i++) {
933 			stream.writeUTF(this.components[i]);
934 			List scenarios = (List) this.allScenarios.get(this.components[i]);
935 			int size = scenarios.size();
936 			stream.writeInt(size);
937 			for (int j=0; j<size; j++) {
938 				final ScenarioResults scenarioResults = (ScenarioResults)scenarios.get(j);
939 				stream.writeInt(scenarioResults.getId());
940 				stream.writeUTF(scenarioResults.getName());
941 				stream.writeUTF(scenarioResults.getLabel());
942 			}
943 		}
944 
945 		// Close
946 		stream.close();
947 		println("	=> performance results general data  written in file " + dataFile); //$NON-NLS-1$
948 	} catch (FileNotFoundException e) {
949 		System.err.println("can't create output file" + dataFile); //$NON-NLS-1$
950 	} catch (IOException e) {
951 		e.printStackTrace();
952 	}
953 }
954 
955 }
956