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.File;
14 import java.io.PrintWriter;
15 import java.io.StringWriter;
16 import java.math.BigDecimal;
17 import java.sql.Connection;
18 import java.sql.DriverManager;
19 import java.sql.ResultSet;
20 import java.sql.SQLException;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.StringTokenizer;
27
28 import org.eclipse.core.runtime.Assert;
29 import org.eclipse.test.internal.performance.PerformanceTestPlugin;
30 import org.eclipse.test.internal.performance.data.Dim;
31 import org.eclipse.test.internal.performance.db.DB;
32 import org.eclipse.test.internal.performance.results.utils.IPerformancesConstants;
33 import org.eclipse.test.internal.performance.results.utils.Util;
34 import org.eclipse.test.performance.Dimension;
35
36 /**
37 * Specific and private implementation of {@link org.eclipse.test.internal.performance.db.DB} class
38 * to get massive results from performance results database.
39 * TODO (frederic) Should be at least a subclass of {@link DB}...
40 */
41 public class DB_Results {
42
43
44 private static final String DEFAULT_DB_BASELINE_PREFIX = "R-";
45 private static final Dim[] NO_DIMENSION = new Dim[0];
46 private static final String[] EMPTY_LIST = new String[0];
47 static final boolean DEBUG = false;
48 static final boolean LOG = false;
49
50 // the two supported DB types
51 private static final String DERBY= "derby"; //$NON-NLS-1$
52 private static final String CLOUDSCAPE= "cloudscape"; //$NON-NLS-1$
53
54 private static DB_Results fgDefault;
55
56 private Connection fConnection;
57 private SQL_Results fSQL;
58 // private boolean fIsEmbedded;
59 private String fDBType; // either "derby" or "cloudscape"
60
61 // Preferences info
62 public static boolean DB_CONNECTION = false;
63 private static String DB_NAME;
64 private static String DB_LOCATION;
65 private static String DB_BASELINE_PREFIX = DEFAULT_DB_BASELINE_PREFIX;
66 private static String DB_VERSION;
67 private static String DB_VERSION_REF;
68
69 /**
70 * Get the name of the database.
71 *
72 * @return The name as a string.
73 */
getDbName()74 public static String getDbName() {
75 if (DB_NAME == null) initDbContants();
76 return DB_NAME;
77 }
78
79 /**
80 * Set the name of the database.
81 *
82 * @param dbName The name as a string.
83 */
setDbName(String dbName)84 public static void setDbName(String dbName) {
85 Assert.isNotNull(dbName);
86 DB_NAME = dbName;
87 }
88
89 /**
90 * Get the location of the database.
91 *
92 * @return The location as a string.
93 */
getDbLocation()94 public static String getDbLocation() {
95 if (!DB_CONNECTION) return null;
96 if (DB_LOCATION == null) initDbContants();
97 return DB_LOCATION;
98 }
99
100 /**
101 * Set the location of the database.
102 *
103 * @param dbLocation The location as a string.
104 */
setDbLocation(String dbLocation)105 public static void setDbLocation(String dbLocation) {
106 Assert.isNotNull(dbLocation);
107 DB_LOCATION = dbLocation;
108 }
109
110 /**
111 * Get the default baseline prefix.
112 *
113 * @return The prefix as a string.
114 */
getDbBaselinePrefix()115 public static String getDbBaselinePrefix() {
116 return DB_BASELINE_PREFIX;
117 }
118
119 /**
120 * Set the baseline prefix of the database.
121 *
122 * @param baselinePrefix The prefix as a string.
123 */
setDbDefaultBaselinePrefix(String baselinePrefix)124 public static void setDbDefaultBaselinePrefix(String baselinePrefix) {
125 Assert.isNotNull(baselinePrefix);
126 Assert.isTrue(baselinePrefix.startsWith(DEFAULT_DB_BASELINE_PREFIX));
127 DB_BASELINE_PREFIX = baselinePrefix;
128 }
129
130 /**
131 * Get the baseline reference version of the database.
132 *
133 * @return The version as a string.
134 */
getDbBaselineRefVersion()135 public static String getDbBaselineRefVersion() {
136 if (DB_VERSION_REF == null) initDbContants();
137 return DB_VERSION_REF;
138 }
139
140 /**
141 * Get the version of the database.
142 *
143 * @return The version as a string.
144 */
getDbVersion()145 public static String getDbVersion() {
146 if (DB_VERSION == null) initDbContants();
147 return DB_VERSION;
148 }
149
150 /**
151 * Set the version of the database.
152 *
153 * @param version The version as a string.
154 */
setDbVersion(String version)155 public static void setDbVersion(String version) {
156 Assert.isNotNull(version);
157 Assert.isTrue(version.startsWith("v3"));
158 DB_VERSION = version;
159 }
160
161 /**
162 * Update the database constants from a new database location.
163 * @param connected Tells whether the database should be connected or not.
164 * @param databaseLocation The database location.
165 * May be a path to a local folder or a net address
166 * (see {@link IPerformancesConstants#NETWORK_DATABASE_LOCATION}).
167 */
updateDbConstants(boolean connected, int eclipseVersion, String databaseLocation)168 public static boolean updateDbConstants(boolean connected, int eclipseVersion, String databaseLocation) {
169 if (DB_CONNECTION != connected || DB_LOCATION == null || DB_NAME == null ||
170 ((databaseLocation == null && !DB_LOCATION.equals(IPerformancesConstants.NETWORK_DATABASE_LOCATION)) ||
171 !DB_LOCATION.equals(databaseLocation)) ||
172 !DB_NAME.equals(IPerformancesConstants.DATABASE_NAME_PREFIX + eclipseVersion)) {
173 shutdown();
174 DB_CONNECTION = connected;
175 DB_LOCATION = databaseLocation == null ? IPerformancesConstants.NETWORK_DATABASE_LOCATION : databaseLocation;
176 DB_NAME = IPerformancesConstants.DATABASE_NAME_PREFIX + eclipseVersion;
177 DB_VERSION = "v" + eclipseVersion;
178 DB_VERSION_REF = "R-3." + (eclipseVersion % 10 - 1);
179 if (connected) {
180 return getDefault().fSQL != null;
181 }
182 }
183 return true;
184 }
185
186 /**
187 * Returns a title including DB version and name.
188 *
189 * @return A title as a string.
190 */
getDbTitle()191 public static String getDbTitle() {
192 if (!DB_CONNECTION) return null;
193 String title = "Eclipse " + DB_VERSION + " - ";
194 if (DB_LOCATION.startsWith("net:")) {
195 title += " Network DB";
196 } else {
197 title += " Local DB";
198 }
199 return title;
200 }
201
202 /**
203 * The list of all the configurations (i.e. machine) stored in the database.
204 */
205 private static String[] CONFIGS;
206
207 /**
208 * The list of all the components stored in the database.
209 */
210 private static String[] COMPONENTS;
211
212 /**
213 * The list of all the builds stored in the database.
214 */
215 private static String[] BUILDS;
216
217 /**
218 * The list of all the dimensions stored in the database.
219 */
220 private static int[] DIMENSIONS;
221
222 /**
223 * The default dimension used to display results (typically in fingerprints).
224 */
225 private static Dim DEFAULT_DIM;
226 private static int DEFAULT_DIM_INDEX;
227
228 /**
229 * The list of all the dimensions displayed while generating results.
230 */
231 private static Dim[] RESULTS_DIMENSIONS;
232
233 /**
234 * The list of all the VMs stored in the database.
235 */
236 private static String[] VMS;
237
238 /**
239 * The list of possible test boxes.
240 * <p>
241 * Only used if no specific configurations are specified
242 * (see {@link PerformanceResults#readAll(String, String[][], String, File, int, org.eclipse.core.runtime.IProgressMonitor)}.
243 * </p>
244 * Note that this is a copy of the the property "eclipse.perf.config.descriptors"
245 * defined in org.eclipse.releng.eclipsebuilder/eclipse/helper.xml file
246 */
247 private static String[] CONFIG_DESCRIPTIONS;
248
249 /**
250 * The list of known Eclipse components.
251 */
252 private final static String[] ECLIPSE_COMPONENTS = {
253 "org.eclipse.ant", //$NON-NLS-1$
254 "org.eclipse.compare", //$NON-NLS-1$
255 "org.eclipse.core", //$NON-NLS-1$
256 "org.eclipse.help", //$NON-NLS-1$
257 "org.eclipse.jdt.core", //$NON-NLS-1$
258 "org.eclipse.jdt.debug", //$NON-NLS-1$
259 "org.eclipse.jdt.text", //$NON-NLS-1$
260 "org.eclipse.jdt.ui", //$NON-NLS-1$
261 "org.eclipse.jface", //$NON-NLS-1$
262 "org.eclipse.osgi", //$NON-NLS-1$
263 "org.eclipse.pde.api.tools", //$NON-NLS-1$
264 "org.eclipse.pde.ui", //$NON-NLS-1$
265 "org.eclipse.swt", //$NON-NLS-1$
266 "org.eclipse.team", //$NON-NLS-1$
267 "org.eclipse.ua", //$NON-NLS-1$
268 "org.eclipse.ui" //$NON-NLS-1$
269 };
270 private static String[] KNOWN_COMPONENTS = ECLIPSE_COMPONENTS;
271
272
273 // Store debug info
274 final static StringWriter DEBUG_STR_WRITER;
275 final static PrintWriter DEBUG_WRITER;
276 static {
277 if (DEBUG) {
278 DEBUG_STR_WRITER= new StringWriter();
279 DEBUG_WRITER= new PrintWriter(DEBUG_STR_WRITER);
280 } else {
281 DEBUG_STR_WRITER= null;
282 DEBUG_WRITER= null;
283 }
284 }
285
286 // Store log info
287 final static StringWriter LOG_STR_WRITER = new StringWriter();
288 final static LogWriter LOG_WRITER = new LogWriter();
289 static class LogWriter extends PrintWriter {
290 long[] starts = new long[10];
291 long[] times = new long[10];
292 StringBuffer[] buffers = new StringBuffer[10];
293 int depth = -1, max = -1;
LogWriter()294 public LogWriter() {
295 super(LOG_STR_WRITER);
296 }
starts(String log)297 void starts(String log) {
298 if (++this.depth >= this.buffers.length) {
299 System.arraycopy(this.times, 0, this.times = new long[this.depth+10], 0, this.depth);
300 System.arraycopy(this.buffers, 0, this.buffers= new StringBuffer[this.depth+10], 0, this.depth);
301 }
302 StringBuffer buffer = this.buffers[this.depth];
303 if (this.buffers[this.depth] == null) buffer = this.buffers[this.depth] = new StringBuffer();
304 buffer.append(log);
305 this.starts[this.depth] = System.currentTimeMillis();
306 if (this.depth > this.max) this.max = this.depth;
307 }
ends(String log)308 void ends(String log) {
309 if (this.depth < 0)
310 throw new RuntimeException("Invalid call to ends (missing corresponding starts call)!"); //$NON-NLS-1$
311 this.buffers[this.depth].append(log);
312 if (this.depth > 0) {
313 this.times[this.depth] += System.currentTimeMillis() - this.starts[this.depth];
314 this.depth--;
315 return;
316 }
317 for (int i=0; i<this.max; i++) {
318 print(this.buffers[i].toString());
319 print(" ( in "); //$NON-NLS-1$
320 print(this.times[this.depth]);
321 println("ms)"); //$NON-NLS-1$
322 }
323 this.depth = this.max = -1;
324 this.starts = new long[10];
325 this.times = new long[10];
326 this.buffers = new StringBuffer[10];
327 }
toString()328 public String toString() {
329 return LOG_STR_WRITER.toString();
330 }
331 }
332
333 // Data storage from queries
334 static String LAST_CURRENT_BUILD, LAST_BASELINE_BUILD;
335 private static int BUILDS_LENGTH;
336 private static String[] SCENARII;
337 private static String[] COMMENTS;
338
339 //---- private implementation
340
341 /**
342 * Private constructor to block instance creation.
343 */
DB_Results()344 private DB_Results() {
345 // empty implementation
346 }
347
getDefault()348 synchronized static DB_Results getDefault() {
349 if (fgDefault == null) {
350 fgDefault= new DB_Results();
351 fgDefault.connect();
352 if (PerformanceTestPlugin.getDefault() == null) {
353 // not started as plugin
354 Runtime.getRuntime().addShutdownHook(
355 new Thread() {
356 public void run() {
357 shutdown();
358 }
359 }
360 );
361 }
362 } else if (fgDefault.fSQL == null) {
363 fgDefault.connect();
364 }
365 return fgDefault;
366 }
367
shutdown()368 public static void shutdown() {
369 if (fgDefault != null) {
370 fgDefault.disconnect();
371 fgDefault= null;
372 BUILDS = null;
373 LAST_BASELINE_BUILD = null;
374 LAST_CURRENT_BUILD = null;
375 DIMENSIONS = null;
376 CONFIGS = null;
377 COMPONENTS = null;
378 SCENARII = null;
379 COMMENTS = null;
380 DB_VERSION = null;
381 DB_VERSION_REF = null;
382 DEFAULT_DIM =null;
383 DEFAULT_DIM_INDEX = -1;
384 RESULTS_DIMENSIONS = null;
385 VMS = null;
386 CONFIG_DESCRIPTIONS = null;
387 KNOWN_COMPONENTS = ECLIPSE_COMPONENTS;
388 }
389 if (DEBUG) {
390 DEBUG_WRITER.println("DB.shutdown"); //$NON-NLS-1$
391 System.out.println(DEBUG_STR_WRITER.toString());
392 }
393 if (LOG) {
394 System.out.println(LOG_STR_WRITER.toString());
395 }
396 }
397
398 /**
399 * Return the build id from a given name.
400 *
401 * @param name The build name (eg. I20070615-1200)
402 * @return The id of the build (ie. the index in the {@link #BUILDS} list)
403 */
getBuildId(String name)404 static int getBuildId(String name) {
405 if (BUILDS == null) return -1;
406 return Arrays.binarySearch(BUILDS, name, Util.BUILD_DATE_COMPARATOR);
407 }
408
409 /**
410 * Return the build name from a given id.
411 *
412 * @param id The build id
413 * @return The name of the build (eg. I20070615-1200)
414 */
getBuildName(int id)415 static String getBuildName(int id) {
416 if (BUILDS == null) return null;
417 return BUILDS[id];
418 }
419
420 /**
421 * Returns all the builds names read from the database.
422 *
423 * @return The list of all builds names matching the scenario pattern used while reading data
424 */
getBuilds()425 public static String[] getBuilds() {
426 if (BUILDS == null) {
427 queryAllVariations("%"); //$NON-NLS-1$
428 }
429 if (BUILDS_LENGTH == 0) return EMPTY_LIST;
430 String[] builds = new String[BUILDS_LENGTH];
431 System.arraycopy(BUILDS, 0, builds, 0, BUILDS_LENGTH);
432 return builds;
433 }
434
435 /**
436 * Returns the number of builds stored int the database.
437 *
438 * @return The number of builds stored in the database.
439 */
getBuildsNumber()440 public static int getBuildsNumber() {
441 if (BUILDS == null) {
442 queryAllVariations("%"); //$NON-NLS-1$
443 }
444 return BUILDS_LENGTH;
445 }
446
447 /**
448 * Get component name from a scenario.
449 *
450 * @param scenarioName The name of the scenario
451 * @return The component name
452 */
getComponentNameFromScenario(String scenarioName)453 static String getComponentNameFromScenario(String scenarioName) {
454 int length = KNOWN_COMPONENTS.length;
455 for (int i=0; i<length; i++) {
456 if (scenarioName.startsWith(KNOWN_COMPONENTS[i])) {
457 return KNOWN_COMPONENTS[i];
458 }
459 }
460 StringTokenizer tokenizer = new StringTokenizer(scenarioName, ".");
461 StringBuffer buffer = new StringBuffer(tokenizer.nextToken());
462 if (tokenizer.hasMoreTokens()) {
463 buffer.append('.');
464 buffer.append(tokenizer.nextToken());
465 if (tokenizer.hasMoreTokens()) {
466 buffer.append('.');
467 buffer.append(tokenizer.nextToken());
468 }
469 }
470 String componentName = buffer.toString();
471 System.err.println(scenarioName+" does not belongs to a known Eclipse component. So use scenario prefix "+componentName+" as component name by default and add it to the know components"); //$NON-NLS-1$
472 System.arraycopy(KNOWN_COMPONENTS, 0, KNOWN_COMPONENTS = new String[length+1], 0, length);
473 KNOWN_COMPONENTS[length] = componentName;
474 return componentName;
475 }
476
477 /**
478 * Get all components read from database.
479 *
480 * @return A list of component names matching the given pattern
481 */
getComponents()482 public static String[] getComponents() {
483 if (COMPONENTS == null) return EMPTY_LIST;
484 int length = COMPONENTS.length;
485 String[] components = new String[length];
486 System.arraycopy(COMPONENTS, 0, components, 0, length);
487 return components;
488 }
489
490 /**
491 * Return the name of the configuration from the given id.
492 *
493 * @param id The index of the configuration in the stored list.
494 * @return The name of the configuration (eg. eclipseperflnx1_R3.3)
495 */
getConfig(int id)496 static String getConfig(int id) {
497 return CONFIGS[id];
498 }
499
500 /**
501 * Get all configurations read from the database.
502 *
503 * @return A list of configuration names
504 */
getConfigs()505 public static String[] getConfigs() {
506 if (CONFIGS == null) return EMPTY_LIST;
507 int length = CONFIGS.length;
508 String[] configs = new String[length];
509 System.arraycopy(CONFIGS, 0, configs, 0, length);
510 return configs;
511 }
512
513 /**
514 * Set the default dimension used for performance results.
515 */
setConfigs(String[] configs)516 public static void setConfigs(String[] configs) {
517 CONFIGS = configs;
518 }
519
520 /**
521 * Get all configurations read from the database.
522 *
523 * @return A list of configuration names
524 */
getConfigDescriptions()525 public static String[] getConfigDescriptions() {
526 if (CONFIG_DESCRIPTIONS == null) {
527 if (CONFIGS == null) return null;
528 int length = CONFIGS.length;
529 CONFIG_DESCRIPTIONS = new String[length];
530 String[][] configDescriptors = PerformanceTestPlugin.getConfigDescriptors();
531 int cdLength = configDescriptors.length;
532 for (int i = 0; i < length; i++) {
533 boolean found = false;
534 for (int j = 0; j < cdLength; j++) {
535 if (configDescriptors[j][0].equals(CONFIGS[i])) {
536 CONFIG_DESCRIPTIONS[i] = configDescriptors[j][1];
537 found = true;
538 break;
539 }
540 }
541 if (!found) {
542 String kind = CONFIGS[i].indexOf("epwin") < 0 ? "Linux" : "Win XP";
543 CONFIG_DESCRIPTIONS[i] = kind+" perf test box "+CONFIGS[i].substring(5);
544 }
545 }
546 }
547 int length = CONFIG_DESCRIPTIONS.length;
548 String[] descriptions = new String[length];
549 System.arraycopy(CONFIG_DESCRIPTIONS, 0, descriptions, 0, length);
550 return descriptions;
551 }
552
553 /**
554 * Set the default dimension used for performance results.
555 */
556 public static void setConfigDescriptions(String[] descriptions) {
557 CONFIG_DESCRIPTIONS = descriptions;
558 }
559
560 /**
561 * Get all dimensions read from the database.
562 *
563 * @return A list of dimensions.
564 */
565 public static Dim[] getDimensions() {
566 if (DIMENSIONS == null) return NO_DIMENSION;
567 int length = DIMENSIONS.length;
568 Dim[] dimensions = new Dim[length];
569 for (int i = 0; i < length; i++) {
570 Dimension dimension = PerformanceTestPlugin.getDimension(DIMENSIONS[i]);
571 if (dimension == null) {
572 throw new RuntimeException("There is an unsupported dimension stored in the database: " +DIMENSIONS[i]);
573 }
574 dimensions[i] = (Dim) dimension;
575 }
576 return dimensions;
577 }
578
579 /**
580 * Return the default dimension used for performance results.
581 *
582 * @return The {@link Dim default dimension}.
583 */
584 public static Dim getDefaultDimension() {
585 if (DEFAULT_DIM == null) {
586 DEFAULT_DIM = (Dim) PerformanceTestPlugin.getDefaultDimension();
587 }
588 return DEFAULT_DIM;
589 }
590
591 /**
592 * Set the default dimension used for performance results.
593 */
594 public static void setDefaultDimension(String dim) {
595 DEFAULT_DIM = (Dim) PerformanceTestPlugin.getDimension(dim);
596 if (DIMENSIONS != null) {
597 DEFAULT_DIM_INDEX = Arrays.binarySearch(DIMENSIONS, DEFAULT_DIM.getId());
598 }
599 }
600
601 public static Dim[] getResultsDimensions() {
602 if (RESULTS_DIMENSIONS == null) {
603 Dimension[] resultsDimensions = PerformanceTestPlugin.getResultsDimensions();
604 int length = resultsDimensions.length;
605 RESULTS_DIMENSIONS = new Dim[length];
606 for (int i = 0; i < length; i++) {
607 RESULTS_DIMENSIONS[i] = (Dim) resultsDimensions[i];
608 }
609 }
610 return RESULTS_DIMENSIONS;
611 }
612
613 /**
614 * Set the default dimension used for performance results.
615 */
616 public static void setResultsDimensions(String[] dimensions) {
617 int length = dimensions.length;
618 RESULTS_DIMENSIONS = new Dim[length];
619 for (int i = 0; i < length; i++) {
620 RESULTS_DIMENSIONS[i] = (Dim) PerformanceTestPlugin.getDimension(dimensions[i]);
621 }
622 }
623
624 /**
625 * Return the default dimension used for performance results.
626 *
627 * @return The {@link Dim default dimension}.
628 */
629 public static int getDefaultDimensionIndex() {
630 if (DEFAULT_DIM == null || DEFAULT_DIM_INDEX == -1) {
631 getDefaultDimension(); // init default dimension
632 getDimensions(); // init dimensions
633 DEFAULT_DIM_INDEX = Arrays.binarySearch(DIMENSIONS, DEFAULT_DIM.getId());
634 }
635 return DEFAULT_DIM_INDEX;
636 }
637
638 /**
639 * Return the ID of the last baseline build before the given date.
640 *
641 * @param date The date the baseline must be run before. If <code>null</code>
642 * return the last baseline build stored in the DB.
643 *
644 * @return the ID of the last baseline build before the given date or
645 * <code>null</code> if none was run before it...
646 */
647 public static String getLastBaselineBuild(String date) {
648 if (BUILDS == null) {
649 queryAllVariations("%"); //$NON-NLS-1$
650 }
651 if (date == null) {
652 if (LAST_BASELINE_BUILD == null) {
653 return BUILDS[0];
654 }
655 return LAST_BASELINE_BUILD;
656 }
657 String lastBaselineBuild = null;
658 for (int i=0; i<BUILDS_LENGTH; i++) {
659 String build = BUILDS[i];
660 if (build.startsWith(DB_VERSION_REF)) {
661 String buildDate = build.substring(build.indexOf('_')+1);
662 if (buildDate.compareTo(date) < 0) {
663 if (lastBaselineBuild == null || build.compareTo(lastBaselineBuild) > 0) {
664 lastBaselineBuild = build;
665 }
666 }
667 }
668 }
669 if (lastBaselineBuild == null) {
670 return BUILDS[0];
671 }
672 return lastBaselineBuild;
673 }
674
675 /**
676 * Return the ID of the last baseline build.
677 *
678 * @return the ID of the last baseline build.
679 */
680 public static String getLastCurrentBuild() {
681 if (BUILDS == null) {
682 queryAllVariations("%"); //$NON-NLS-1$
683 }
684 return LAST_CURRENT_BUILD;
685 }
686
687 /**
688 * Returns all the scenarios names read from the database.
689 *
690 * @return The list of all scenarios matching the pattern for a given build.
691 * @see #internalQueryBuildScenarios(String, String)
692 */
693 public static List getScenarios() {
694 return Arrays.asList(SCENARII);
695 }
696
697 /**
698 * Init the constants if necessary.
699 */
700 public static void initDbContants() {
701 if (DB_LOCATION == null) {
702 DB_LOCATION = PerformanceTestPlugin.getDBLocation();
703 if (DB_LOCATION == null) {
704 new RuntimeException("Cannot connect to the DB without a location!");
705 }
706 }
707 if (DB_NAME == null) {
708 DB_NAME = PerformanceTestPlugin.getDBName();
709 if (DB_NAME == null) {
710 new RuntimeException("Cannot connect to the DB without a name!");
711 }
712 }
713 if (DB_VERSION == null) {
714 DB_VERSION = "v" + DB_NAME.substring(DB_NAME.length()-2);
715 DB_VERSION_REF = "R-3."+(Character.digit(DB_NAME.charAt(DB_NAME.length()-1), 10)-1);
716 }
717 }
718
719 /**
720 * Get all scenarios read from database.
721 *
722 * @return A list of all scenario names matching the default pattern
723 */
724 public static Map queryAllScenarios() {
725 return getDefault().internalQueryBuildScenarios("%", null); //$NON-NLS-1$
726 }
727
728 /**
729 * Get all scenarios read from database matching a given pattern.
730 * Note that all scenarios are returned if the pattern is <code>null</code>.
731 *
732 * @param scenarioPattern The pattern of the requested scenarios
733 * @return A map of all scenarios matching the given pattern.
734 * The map keys are component names and values are the scenarios list for
735 * each component.
736 */
737 static Map queryAllScenarios(String scenarioPattern) {
738 String pattern = scenarioPattern==null ? "%" : scenarioPattern; //$NON-NLS-1$
739 return getDefault().internalQueryBuildScenarios(pattern, null);
740 }
741
742 /**
743 * Get all scenarios read from database matching a given pattern.
744 * Note that all scenarios are returned if the pattern is <code>null</code>.
745 *
746 * @param scenarioPattern The pattern of the requested scenarios
747 * @param buildName The build name
748 * @return A list of scenario names matching the given pattern
749 */
750 static Map queryAllScenarios(String scenarioPattern, String buildName) {
751 return getDefault().internalQueryBuildScenarios(scenarioPattern, buildName);
752 }
753
754 /**
755 * Get all variations read from database matching a given configuration pattern.
756 *
757 * @param configPattern The pattern of the requested configurations
758 */
759 static void queryAllVariations(String configPattern) {
760 getDefault().internalQueryAllVariations(configPattern);
761 }
762
763 /**
764 * Get all summaries from DB for a given scenario and configuration pattern
765 *
766 * @param scenarioResults The scenario results where to store data
767 * @param configPattern The configuration pattern concerned by the query
768 * @param builds All builds to get summaries, if <code>null</code>, then all DB
769 * builds will be concerned.
770 */
771 static void queryScenarioSummaries(ScenarioResults scenarioResults, String configPattern, String[] builds) {
772 getDefault().internalQueryScenarioSummaries(scenarioResults, configPattern, builds);
773 }
774
775 /**
776 * Query and store all values for given scenario results
777 *
778 * @param scenarioResults The scenario results where the values has to be put
779 * @param configPattern The pattern of the configuration concerned by the query
780 * @param buildName Name of the last build on which data were stored locally
781 *
782 */
783 static void queryScenarioValues(ScenarioResults scenarioResults, String configPattern, String buildName) {
784 getDefault().internalQueryScenarioValues(scenarioResults, configPattern, buildName);
785 }
786
787 /**
788 * dbloc= embed in home directory
789 * dbloc=/tmp/performance embed given location
790 * dbloc=net://localhost connect to local server
791 * dbloc=net://www.eclipse.org connect to remove server
792 */
793 private void connect() {
794
795 if (this.fConnection != null || !DB_CONNECTION)
796 return;
797
798 if (DEBUG) DriverManager.setLogWriter(new PrintWriter(System.out));
799
800 // Init DB location and name if not already done
801 if (DB_LOCATION == null) {
802 initDbContants();
803 }
804
805 String url = null;
806 java.util.Properties info = new java.util.Properties();
807
808 if (DEBUG) {
809 DEBUG_WRITER.println();
810 DEBUG_WRITER.println("==========================================================="); //$NON-NLS-1$
811 DEBUG_WRITER.println("Database debug information stored while processing"); //$NON-NLS-1$
812 }
813 if (LOG) {
814 LOG_WRITER.println();
815 LOG_WRITER.println("==========================================================="); //$NON-NLS-1$
816 LOG_WRITER.println("Database log information stored while processing"); //$NON-NLS-1$
817 }
818
819 this.fDBType = DERBY; // assume we are using Derby
820 try {
821 if (DB_LOCATION.startsWith("net://")) { //$NON-NLS-1$
822 // remote
823 // fIsEmbedded = false;
824 // connect over network
825 if (DEBUG)
826 DEBUG_WRITER.println("Trying to connect over network..."); //$NON-NLS-1$
827 Class.forName("com.ibm.db2.jcc.DB2Driver"); //$NON-NLS-1$
828 info.put("user", PerformanceTestPlugin.getDBUser()); //$NON-NLS-1$
829 info.put("password", PerformanceTestPlugin.getDBPassword()); //$NON-NLS-1$
830 info.put("retrieveMessagesFromServerOnGetMessage", "true"); //$NON-NLS-1$ //$NON-NLS-2$
831 info.put("create", "true"); //$NON-NLS-1$ //$NON-NLS-2$
832 url = DB_LOCATION + '/' + DB_NAME;
833 } else if (DB_LOCATION.startsWith("//")) { //$NON-NLS-1$
834 // remote
835 // fIsEmbedded = false;
836 // connect over network
837 if (DEBUG)
838 DEBUG_WRITER.println("Trying to connect over network..."); //$NON-NLS-1$
839 Class.forName("org.apache.derby.jdbc.ClientDriver"); //$NON-NLS-1$
840 info.put("user", PerformanceTestPlugin.getDBUser()); //$NON-NLS-1$
841 info.put("password", PerformanceTestPlugin.getDBPassword()); //$NON-NLS-1$
842 info.put("create", "true"); //$NON-NLS-1$ //$NON-NLS-2$
843 url = DB_LOCATION + '/' + DB_NAME;
844 } else {
845 // workaround for Derby issue:
846 // http://nagoya.apache.org/jira/browse/DERBY-1
847 if ("Mac OS X".equals(System.getProperty("os.name"))) //$NON-NLS-1$//$NON-NLS-2$
848 System.setProperty("derby.storage.fileSyncTransactionLog", "true"); //$NON-NLS-1$ //$NON-NLS-2$
849
850 // embedded
851 try {
852 Class.forName("org.apache.derby.jdbc.EmbeddedDriver"); //$NON-NLS-1$
853 // fIsEmbedded = true;
854 } catch (ClassNotFoundException e) {
855 Class.forName("com.ihost.cs.jdbc.CloudscapeDriver"); //$NON-NLS-1$
856 this.fDBType = CLOUDSCAPE;
857 }
858 if (DEBUG)
859 DEBUG_WRITER.println("Loaded embedded " + this.fDBType); //$NON-NLS-1$
860 File f;
861 if (DB_LOCATION.length() == 0) {
862 String user_home = System.getProperty("user.home"); //$NON-NLS-1$
863 if (user_home == null)
864 return;
865 f = new File(user_home, this.fDBType);
866 } else
867 f = new File(DB_LOCATION);
868 url = new File(f, DB_NAME).getAbsolutePath();
869 info.put("user", PerformanceTestPlugin.getDBUser()); //$NON-NLS-1$
870 info.put("password", PerformanceTestPlugin.getDBPassword()); //$NON-NLS-1$
871 info.put("create", "true"); //$NON-NLS-1$ //$NON-NLS-2$
872 }
873 try {
874 this.fConnection = DriverManager.getConnection("jdbc:" + this.fDBType + ":" + url, info); //$NON-NLS-1$ //$NON-NLS-2$
875 } catch (SQLException e) {
876 if ("08001".equals(e.getSQLState()) && DERBY.equals(this.fDBType)) { //$NON-NLS-1$
877 if (DEBUG)
878 DEBUG_WRITER.println("DriverManager.getConnection failed; retrying for cloudscape"); //$NON-NLS-1$
879 // try Cloudscape
880 this.fDBType = CLOUDSCAPE;
881 this.fConnection = DriverManager.getConnection("jdbc:" + this.fDBType + ":" + url, info); //$NON-NLS-1$ //$NON-NLS-2$
882 } else
883 throw e;
884 }
885 if (DEBUG)
886 DEBUG_WRITER.println("connect succeeded!"); //$NON-NLS-1$
887
888 this.fConnection.setAutoCommit(false);
889 this.fSQL = new SQL_Results(this.fConnection);
890 this.fConnection.commit();
891
892 } catch (SQLException ex) {
893 PerformanceTestPlugin.logError(ex.getMessage());
894
895 } catch (ClassNotFoundException e) {
896 PerformanceTestPlugin.log(e);
897 }
898 }
899
900 private void disconnect() {
901 if (DEBUG)
902 DEBUG_WRITER.println("disconnecting from DB"); //$NON-NLS-1$
903 if (this.fSQL != null) {
904 try {
905 this.fSQL.dispose();
906 } catch (SQLException e1) {
907 PerformanceTestPlugin.log(e1);
908 }
909 this.fSQL = null;
910 }
911 if (this.fConnection != null) {
912 try {
913 this.fConnection.commit();
914 } catch (SQLException e) {
915 PerformanceTestPlugin.log(e);
916 }
917 try {
918 this.fConnection.close();
919 } catch (SQLException e) {
920 PerformanceTestPlugin.log(e);
921 }
922 this.fConnection = null;
923 }
924
925 /*
926 if (fIsEmbedded) {
927 try {
928 DriverManager.getConnection("jdbc:" + fDBType + ":;shutdown=true"); //$NON-NLS-1$ //$NON-NLS-2$
929 } catch (SQLException e) {
930 String message = e.getMessage();
931 if (message.indexOf("system shutdown.") < 0) //$NON-NLS-1$
932 e.printStackTrace();
933 }
934 }
935 */
936 }
937
938 /*
939 * Return the index of the given configuration in the stored list.
940 */
941 private int getConfigId(String config) {
942 if (CONFIGS == null) return -1;
943 return Arrays.binarySearch(CONFIGS, config);
944 }
945
946 SQL_Results getSQL() {
947 return this.fSQL;
948 }
949
950 /*
951 * Query all comments from database
952 */
953 private void internalQueryAllComments() {
954 if (this.fSQL == null) return;
955 if (COMMENTS != null) return;
956 long start = System.currentTimeMillis();
957 if (DEBUG) DEBUG_WRITER.print(" [DB query all comments..."); //$NON-NLS-1$
958 ResultSet result = null;
959 try {
960 String[] comments = null;
961 result = this.fSQL.queryAllComments();
962 while (result.next()) {
963 int commentID = result.getInt(1);
964 // Ignore kind as there's only one
965 // int commentKind = result.getInt(2);
966 String comment = result.getString(3);
967 if (comments == null) {
968 comments = new String[commentID+10];
969 } else if (commentID >= comments.length) {
970 int length = comments.length;
971 System.arraycopy(comments, 0, comments = new String[commentID+10], 0, length);
972 }
973 comments[commentID] = comment;
974 }
975 COMMENTS = comments;
976 } catch (SQLException e) {
977 PerformanceTestPlugin.log(e);
978 } finally {
979 if (result != null) {
980 try {
981 result.close();
982 } catch (SQLException e1) {
983 // ignored
984 }
985 }
986 if (DEBUG) DEBUG_WRITER.println("done in " + (System.currentTimeMillis() - start) + "ms]"); //$NON-NLS-1$ //$NON-NLS-2$
987 }
988 }
989
990 /*
991 * Query all variations. This method stores all config and build names.
992 */
993 private void internalQueryAllVariations(String configPattern) {
994 if (this.fSQL == null) return;
995 if (BUILDS != null) return;
996 long start = System.currentTimeMillis();
997 if (DEBUG) {
998 DEBUG_WRITER.print(" - DB query all variations for configuration pattern: "+configPattern); //$NON-NLS-1$
999 DEBUG_WRITER.print("..."); //$NON-NLS-1$
1000 }
1001 ResultSet result = null;
1002 try {
1003 CONFIGS = null;
1004 BUILDS = null;
1005 BUILDS_LENGTH = 0;
1006 result = this.fSQL.queryAllVariations(configPattern);
1007 while (result.next()) {
1008 String variation = result.getString(1); // something like "||build=I20070615-1200||config=eclipseperfwin2_R3.3||jvm=sun|"
1009 StringTokenizer tokenizer = new StringTokenizer(variation, "=|"); //$NON-NLS-1$
1010 tokenizer.nextToken(); // 'build'
1011 storeBuildName(tokenizer.nextToken()); // 'I20070615-1200'
1012 tokenizer.nextToken(); // 'config'
1013 storeConfig(tokenizer.nextToken()); // 'eclipseperfwin2_R3.3'
1014 tokenizer.nextToken(); // 'jvm'
1015 storeVm(tokenizer.nextToken()); // 'sun'
1016 }
1017 if (BUILDS_LENGTH == 0) {
1018 BUILDS = EMPTY_LIST;
1019 }
1020 } catch (SQLException e) {
1021 PerformanceTestPlugin.log(e);
1022 } finally {
1023 if (result != null) {
1024 try {
1025 result.close();
1026 } catch (SQLException e1) {
1027 // ignored
1028 }
1029 }
1030 if (DEBUG) DEBUG_WRITER.println("done in " + (System.currentTimeMillis() - start) + "ms]"); //$NON-NLS-1$ //$NON-NLS-2$
1031 }
1032 }
1033
1034 private Map internalQueryBuildScenarios(String scenarioPattern, String buildName) {
1035 if (this.fSQL == null) return null;
1036 long start = System.currentTimeMillis();
1037 if (DEBUG) {
1038 DEBUG_WRITER.print(" - DB query all scenarios"); //$NON-NLS-1$
1039 if (scenarioPattern != null) DEBUG_WRITER.print(" with pattern "+scenarioPattern); //$NON-NLS-1$
1040 if (buildName != null) DEBUG_WRITER.print(" for build: "+buildName); //$NON-NLS-1$
1041 }
1042 ResultSet result = null;
1043 Map allScenarios = new HashMap();
1044 try {
1045 if (buildName == null) {
1046 result = this.fSQL.queryBuildAllScenarios(scenarioPattern);
1047 } else {
1048 result = this.fSQL.queryBuildScenarios(scenarioPattern, buildName);
1049 }
1050 int previousId = -1;
1051 List scenarios = null;
1052 List scenariosNames = new ArrayList();
1053 for (int i = 0; result.next(); i++) {
1054 int id = result.getInt(1);
1055 String name = result.getString(2);
1056 scenariosNames.add(name);
1057 String shortName = result.getString(3);
1058 int component_id = storeComponent(getComponentNameFromScenario(name));
1059 if (component_id != previousId) {
1060 allScenarios.put(COMPONENTS[component_id], scenarios = new ArrayList());
1061 previousId = component_id;
1062 }
1063 scenarios.add(new ScenarioResults(id, name, shortName));
1064 }
1065 SCENARII = new String[scenariosNames.size()];
1066 scenariosNames.toArray(SCENARII);
1067 } catch (SQLException e) {
1068 PerformanceTestPlugin.log(e);
1069 } finally {
1070 if (result != null) {
1071 try {
1072 result.close();
1073 } catch (SQLException e1) { // ignored
1074 }
1075 }
1076 if (DEBUG) DEBUG_WRITER.println("done in " + (System.currentTimeMillis() - start) + "ms]"); //$NON-NLS-1$ //$NON-NLS-2$
1077 }
1078 return allScenarios;
1079 }
1080
1081 private void internalQueryScenarioValues(ScenarioResults scenarioResults, String configPattern, String buildName) {
1082 if (this.fSQL == null) return;
1083 if (DEBUG) {
1084 DEBUG_WRITER.print(" - DB query all data points for config pattern: "+configPattern+" for scenario: " + scenarioResults.getShortName()); //$NON-NLS-1$ //$NON-NLS-2$
1085 if (buildName != null) DEBUG_WRITER.print(" for build: "+buildName); //$NON-NLS-1$
1086 }
1087 internalQueryAllVariations(configPattern); // need to read all variations to have all build names
1088 ResultSet result = null;
1089 try {
1090 int count = 0;
1091
1092 result = buildName == null
1093 ? this.fSQL.queryScenarioDataPoints(configPattern, scenarioResults.getId())
1094 : this.fSQL.queryScenarioBuildDataPoints(configPattern, scenarioResults.getId(), buildName);
1095 while (result.next()) {
1096 int dp_id = result.getInt(1);
1097 int step = result.getInt(2);
1098 String variation = result.getString(3); // something like "|build=I20070615-1200||config=eclipseperfwin2_R3.3||jvm=sun|"
1099 StringTokenizer tokenizer = new StringTokenizer(variation, "=|"); //$NON-NLS-1$
1100 tokenizer.nextToken(); // 'build'
1101 int build_id = getBuildId(tokenizer.nextToken()); // 'I20070615-1200'
1102 tokenizer.nextToken(); // 'config'
1103 int config_id = getConfigId(tokenizer.nextToken()); // 'eclipseperflnx3'
1104 ResultSet rs2 = this.fSQL.queryDimScalars(dp_id);
1105 while (rs2.next()) {
1106 int dim_id = rs2.getInt(1);
1107 storeDimension(dim_id);
1108 BigDecimal decimalValue = rs2.getBigDecimal(2);
1109 long value = decimalValue.longValue();
1110 if (build_id >= 0) { // build id may be negative (i.e. not stored in the array) if new run starts while we're getting results
1111 scenarioResults.setValue(build_id, dim_id, config_id, step, value);
1112 }
1113 count++;
1114 }
1115 }
1116 if (LOG) LOG_WRITER.ends(" -> " + count + " values read"); //$NON-NLS-1$ //$NON-NLS-2$
1117 } catch (SQLException e) {
1118 PerformanceTestPlugin.log(e);
1119 } finally {
1120 if (result != null) {
1121 try {
1122 result.close();
1123 } catch (SQLException e1) {
1124 // ignored
1125 }
1126 }
1127 }
1128 }
1129
1130 private void internalQueryScenarioSummaries(ScenarioResults scenarioResults, String config, String[] builds) {
1131 if (this.fSQL == null) return;
1132 long start = System.currentTimeMillis();
1133 if (DEBUG) {
1134 DEBUG_WRITER.print(" - DB query all summaries for scenario '"+scenarioResults.getShortName()+"' of '"+config+"' config"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
1135 }
1136 internalQueryAllComments();
1137 ResultSet result = null;
1138 try {
1139 int scenarioID = scenarioResults.getId();
1140 // First try to get summaries of elapsed process dimension
1141 result = this.fSQL.queryScenarioSummaries(scenarioID, config, builds);
1142 while (result.next()) {
1143 String variation = result.getString(1); // something like "|build=I20070615-1200||config=eclipseperfwin2_R3.3||jvm=sun|"
1144 int summaryKind = result.getShort(2);
1145 int comment_id = result.getInt(3);
1146 int dim_id = result.getInt(4);
1147 if (dim_id != 0) storeDimension(dim_id);
1148 StringTokenizer tokenizer = new StringTokenizer(variation, "=|"); //$NON-NLS-1$
1149 tokenizer.nextToken(); // 'build'
1150 String buildName = tokenizer.nextToken(); // 'I20070615-1200'
1151 tokenizer.nextToken(); // 'config'
1152 int config_id = getConfigId(tokenizer.nextToken()); // 'eclipseperflnx3'
1153 int build_id = getBuildId(buildName);
1154 if (build_id >= 0) {
1155 scenarioResults.setInfos(config_id, build_id, dim_id==0?-1:summaryKind, COMMENTS[comment_id]);
1156 }
1157 }
1158 } catch (SQLException e) {
1159 PerformanceTestPlugin.log(e);
1160 } finally {
1161 if (result != null) {
1162 try {
1163 result.close();
1164 } catch (SQLException e1) {
1165 // ignored
1166 }
1167 }
1168 if (DEBUG) DEBUG_WRITER.println("done in " + (System.currentTimeMillis() - start) + "ms]"); //$NON-NLS-1$ //$NON-NLS-2$
1169 }
1170 }
1171
1172 /*
1173 * Store a build name in the dynamic list.
1174 * The list is sorted alphabetically.
1175 */
1176 private int storeBuildName(String build) {
1177 boolean isVersion = build.startsWith(DB_BASELINE_PREFIX);
1178 if (BUILDS == null) {
1179 BUILDS = new String[1];
1180 BUILDS[BUILDS_LENGTH++] = build;
1181 if (isVersion) {
1182 LAST_BASELINE_BUILD = build;
1183 } else {
1184 LAST_CURRENT_BUILD = build;
1185 }
1186 return 0;
1187 }
1188 int idx = Arrays.binarySearch(BUILDS, build, Util.BUILD_DATE_COMPARATOR);
1189 if (idx >= 0) return idx;
1190 int index = -idx-1;
1191 int length = BUILDS.length;
1192 if (BUILDS_LENGTH == length) {
1193 String[] array = new String[length+1];
1194 if (index > 0) System.arraycopy(BUILDS, 0, array, 0, index);
1195 array[index] = build;
1196 if (index < length) {
1197 System.arraycopy(BUILDS, index, array, index+1, length-index);
1198 }
1199 BUILDS = array;
1200 }
1201 BUILDS_LENGTH++;
1202 if (isVersion) {
1203 if (LAST_BASELINE_BUILD == null || LAST_CURRENT_BUILD == null) {
1204 LAST_BASELINE_BUILD = build;
1205 } else {
1206 String buildDate = LAST_CURRENT_BUILD.substring(1, 9)+LAST_CURRENT_BUILD.substring(10, LAST_CURRENT_BUILD.length());
1207 String baselineDate = LAST_BASELINE_BUILD.substring(LAST_BASELINE_BUILD.indexOf('_')+1);
1208 if (build.compareTo(LAST_BASELINE_BUILD) > 0 && baselineDate.compareTo(buildDate) < 0) {
1209 LAST_BASELINE_BUILD = build;
1210 }
1211 }
1212 } else {
1213 if (LAST_CURRENT_BUILD == null || build.substring(1).compareTo(LAST_CURRENT_BUILD.substring(1)) >= 0) {
1214 LAST_CURRENT_BUILD = build;
1215 }
1216 }
1217 return index;
1218 }
1219
1220 /*
1221 * Store a configuration in the dynamic list.
1222 * The list is sorted alphabetically.
1223 */
storeConfig(String config)1224 private int storeConfig(String config) {
1225 if (CONFIGS== null) {
1226 CONFIGS= new String[1];
1227 CONFIGS[0] = config;
1228 return 0;
1229 }
1230 int idx = Arrays.binarySearch(CONFIGS, config);
1231 if (idx >= 0) return idx;
1232 int length = CONFIGS.length;
1233 System.arraycopy(CONFIGS, 0, CONFIGS = new String[length+1], 0, length);
1234 CONFIGS[length] = config;
1235 Arrays.sort(CONFIGS);
1236 return length;
1237 }
1238
1239 /*
1240 * Store a component in the dynamic list. The list is sorted alphabetically.
1241 * Note that the array is rebuilt each time a new component is discovered
1242 * as this does not happen so often (e.g. eclipse only has 10 components).
1243 */
storeComponent(String component)1244 private int storeComponent(String component) {
1245 if (COMPONENTS== null) {
1246 COMPONENTS= new String[1];
1247 COMPONENTS[0] = component;
1248 return 0;
1249 }
1250 int idx = Arrays.binarySearch(COMPONENTS, component);
1251 if (idx >= 0) return idx;
1252 int length = COMPONENTS.length;
1253 System.arraycopy(COMPONENTS, 0, COMPONENTS = new String[length+1], 0, length);
1254 COMPONENTS[length] = component;
1255 Arrays.sort(COMPONENTS);
1256 return length;
1257 }
1258
1259 /*
1260 * Store a dimension in the dynamic list. The list is sorted in ascending order.
1261 * Note that the array is rebuilt each time a new dimension is discovered
1262 * as this does not happen so often (e.g. eclipse only stores two dimensions).
1263 */
storeDimension(int id)1264 public static int storeDimension(int id) {
1265 if (DIMENSIONS == null) {
1266 DIMENSIONS = new int[1];
1267 DIMENSIONS[0] = id;
1268 return 0;
1269 }
1270 int idx = Arrays.binarySearch(DIMENSIONS, id);
1271 if (idx >= 0) return idx;
1272 int length = DIMENSIONS.length;
1273 System.arraycopy(DIMENSIONS, 0, DIMENSIONS = new int[length+1], 0, length);
1274 DIMENSIONS[length] = id;
1275 Arrays.sort(DIMENSIONS);
1276 return length;
1277 }
1278
1279 /*
1280 * Store a dimension in the dynamic list. The list is sorted alphabetically.
1281 * Note that the array is rebuilt each time a new dimension is discovered
1282 * as this does not happen so often (e.g. eclipse only stores two dimensions).
1283 */
storeVm(String vm)1284 private int storeVm(String vm) {
1285 if (VMS == null) {
1286 VMS = new String[1];
1287 VMS[0] = vm;
1288 return 0;
1289 }
1290 int idx = Arrays.binarySearch(VMS, vm);
1291 if (idx >= 0) return idx;
1292 int length = VMS.length;
1293 System.arraycopy(VMS, 0, VMS = new String[length+1], 0, length);
1294 VMS[length] = vm;
1295 Arrays.sort(VMS);
1296 return length;
1297 }
1298
1299 }
1300