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 *******************************************************************************/ 11package org.eclipse.test.internal.performance.results.db; 12 13import java.io.File; 14import java.io.PrintWriter; 15import java.io.StringWriter; 16import java.math.BigDecimal; 17import java.sql.Connection; 18import java.sql.DriverManager; 19import java.sql.ResultSet; 20import java.sql.SQLException; 21import java.util.ArrayList; 22import java.util.Arrays; 23import java.util.HashMap; 24import java.util.List; 25import java.util.Map; 26import java.util.StringTokenizer; 27 28import org.eclipse.core.runtime.Assert; 29import org.eclipse.test.internal.performance.PerformanceTestPlugin; 30import org.eclipse.test.internal.performance.data.Dim; 31import org.eclipse.test.internal.performance.db.DB; 32import org.eclipse.test.internal.performance.results.utils.IPerformancesConstants; 33import org.eclipse.test.internal.performance.results.utils.Util; 34import 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 */ 41public 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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; 294 public LogWriter() { 295 super(LOG_STR_WRITER); 296 } 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 } 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 } 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 */ 344 private DB_Results() { 345 // empty implementation 346 } 347 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 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 */ 404static 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 */ 415static 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 */ 425public 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 */ 440public 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 */ 453static 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 */ 482public 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 */ 496static 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 */ 505public 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 */ 516public 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 */ 525public 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 */ 556public 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 */ 565public 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 */ 584public 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 */ 594public 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 601public 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 */ 616public 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 */ 629public 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 */ 647public 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 */ 680public 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 */ 693public static List getScenarios() { 694 return Arrays.asList(SCENARII); 695} 696 697/** 698 * Init the constants if necessary. 699 */ 700public 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 */ 724public 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 */ 737static 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 */ 750static 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 */ 759static 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 */ 771static 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*/ 783static 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 */ 793private 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 900private 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 */ 941private int getConfigId(String config) { 942 if (CONFIGS == null) return -1; 943 return Arrays.binarySearch(CONFIGS, config); 944} 945 946SQL_Results getSQL() { 947 return this.fSQL; 948} 949 950/* 951 * Query all comments from database 952 */ 953private 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 */ 993private 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 1034private 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 1081private 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 1130private 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 */ 1176private 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 */ 1224private 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 */ 1244private 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 */ 1264public 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 */ 1284private 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