001/* 002 * Copyright (c) 2008-2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: crawl $' 006 * '$Date: 2017-07-21 16:59:52 +0000 (Fri, 21 Jul 2017) $' 007 * '$Revision: 34591 $' 008 * 009 * Permission is hereby granted, without written agreement and without 010 * license or royalty fees, to use, copy, modify, and distribute this 011 * software and its documentation for any purpose, provided that the above 012 * copyright notice and the following two paragraphs appear in all copies 013 * of this software. 014 * 015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 019 * SUCH DAMAGE. 020 * 021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 026 * ENHANCEMENTS, OR MODIFICATIONS. 027 * 028 */ 029 030package org.kepler; 031 032import java.io.File; 033import java.io.InputStream; 034import java.lang.reflect.Constructor; 035import java.net.URL; 036import java.util.Iterator; 037import java.util.LinkedList; 038import java.util.List; 039import java.util.Map; 040 041import org.apache.commons.io.FileUtils; 042import org.apache.commons.logging.Log; 043import org.apache.commons.logging.LogFactory; 044import org.kepler.build.util.Version; 045import org.kepler.gui.kar.ImportModuleDependenciesAction; 046import org.kepler.kar.KAREntry; 047import org.kepler.kar.KARFile; 048import org.kepler.kar.handlers.ActorMetadataKAREntryHandler; 049import org.kepler.objectmanager.lsid.KeplerLSID; 050import org.kepler.util.ParseWorkflowUtil; 051 052import ptolemy.actor.Manager; 053import ptolemy.actor.gui.Configuration; 054import ptolemy.actor.gui.ConfigurationApplication; 055import ptolemy.actor.gui.Effigy; 056import ptolemy.actor.gui.ModelDirectory; 057import ptolemy.actor.gui.PtolemyEffigy; 058import ptolemy.actor.gui.PtolemyPreferences; 059import ptolemy.actor.gui.TableauFrame; 060import ptolemy.data.expr.StringParameter; 061import ptolemy.kernel.attributes.VersionAttribute; 062import ptolemy.kernel.util.Attribute; 063import ptolemy.kernel.util.IllegalActionException; 064import ptolemy.kernel.util.InternalErrorException; 065import ptolemy.kernel.util.KernelException; 066import ptolemy.kernel.util.NamedObj; 067import ptolemy.kernel.util.StringAttribute; 068import ptolemy.kernel.util.Workspace; 069import ptolemy.moml.MoMLParser; 070import ptolemy.moml.filter.BackwardCompatibility; 071import ptolemy.util.MessageHandler; 072import ptolemy.util.StringUtilities; 073 074/** 075 * This class is the startup class for all non-gui applications of kepler. It 076 * handles the command line interface and executes the proper base class in 077 * ptolemy. 078 * 079 * This application supports executing kepler when the -nogui AND -cache command 080 * line flags are set. See org.kepler.Kepler.parseArgsAndRun() to see how this 081 * application is launched. 082 * 083 * The reason this application needs to exist is so Kepler can startup a 084 * headless runtime with the configuration system in place. Without the 085 * configuration, the cache cannot be started. This is working around a 086 * limitation in Ptolemy where the GUI generally needs to be instantiated in 087 * order to get an instance of Configuration. This class could possibly go away 088 * after the new configuration system comes on line if we can pull the 089 * configuration options needed for the cache. 090 * 091 * You can execute this class with the following command: ./kepler.sh -runwf 092 * -nogui -cache workflow.xml 093 * 094 * To generate the kepler.sh script, run 'ant startup-script' from the 095 * build-area. 096 * 097 * @author Chad Berkley, Christopher Brooks, Dan Crawl 098 * @version $Id: KeplerConfigurationApplication.java 33533 2015-07-13 20:22:49Z 099 * crawl $ 100 */ 101public class KeplerConfigurationApplication extends ConfigurationApplication { 102 private static final Log log = LogFactory.getLog(KeplerConfigurationApplication.class.getName()); 103 private static final boolean isDebugging = log.isDebugEnabled(); 104 105 /** 106 * constructor. @See ptolemy.actor.gui.ConfigurationApplication for more 107 * information. 108 * 109 * @param args 110 */ 111 public KeplerConfigurationApplication(String[] args) throws Exception { 112 if (isDebugging) 113 log.debug("construct: " + args); 114 _initializeApplication(); 115 _basePath = "ptolemy/configs"; 116 117 // Create a parser to use. 118 _parser = new MoMLParser(); 119 120 // We set the list of MoMLFilters to handle Backward Compatibility. 121 MoMLParser.setMoMLFilters(BackwardCompatibility.allFilters()); 122 123 MessageHandler.setMessageHandler(new /* Graphical */MessageHandler()); 124 125 try { 126 java.util.Locale.setDefault(java.util.Locale.US); 127 } catch (java.security.AccessControlException accessControl) { 128 // FIXME: If the application is run under Web Start, then this 129 // exception will be thrown. 130 } 131 132 try { 133 _configuration = readConfiguration(specToURL(args[0])); 134 135 // replace kepler display actors into Discard if they are set in 136 // configuration xml. 137 _setGUIConfiguration(_configuration, "_keplerDisplayClassesWithRedirect"); 138 _setGUIConfiguration(_configuration, "_keplerDisplayClassesNoRedirect"); 139 140 /* 141 * List momlFilters = MoMLParser.getMoMLFilters(); if (momlFilters 142 * != null) { Iterator filters = momlFilters.iterator(); while 143 * (filters.hasNext()) { MoMLFilter filter = (MoMLFilter) 144 * filters.next(); if (filter instanceof RemoveGraphicalClasses) { 145 * RemoveGraphicalClasses rgc = (RemoveGraphicalClasses) filter; // 146 * rgc.put("org.geon.BrowserDisplay", // 147 * "ptolemy.actor.lib.Discard"); break; } } } 148 */ 149 150 // initialize the KAR entries 151 String fileName = args[args.length - 1]; 152 if (isDebugging) 153 log.debug(fileName); 154 155 // parse the command-line arguments 156 parseArgs(args); 157 158 // add special parameters to configuration 159 160 if (_server) { 161 // if the server flag is set, kepler is running on a server so 162 // the _server configuration attribute will be set so that other 163 // processes can behave accordingly 164 StringAttribute sa = new StringAttribute(_configuration, "_server"); 165 sa.setExpression("true"); 166 167 } 168 169 if (_repository != null) { 170 StringAttribute sa = new StringAttribute(_configuration, "_repository"); 171 sa.setExpression(_repository); 172 } 173 if (_domain != null) { 174 StringAttribute sa = new StringAttribute(_configuration, "_domain"); 175 sa.setExpression(_domain); 176 } 177 if (_username != null) { 178 StringAttribute sa = new StringAttribute(_configuration, "_username"); 179 sa.setExpression(_username); 180 } 181 if (_password != null) { 182 StringAttribute sa = new StringAttribute(_configuration, "_password"); 183 sa.setExpression(_password); 184 } 185 186 // extract the workflow moml from the kar 187 188 if (fileName.trim().endsWith(".kar")) { 189 if (isDebugging) { 190 log.debug("Extracting workflow from KAR"); 191 } 192 String karFileName = fileName; 193 File file = new File(karFileName); 194 KARFile karf = null; 195 try { 196 karf = new KARFile(file); 197 198 // see if KAR is openable 199 if (!karf.isOpenable()) { 200 201 // check dependencies. 202 Map<String, Version> missingDeps = karf.getMissingDependencies(); 203 if (!missingDeps.isEmpty()) { 204 205 // print out the missing dependencies 206 System.out.println("WARNING: Missing module dependencies:"); 207 for (Map.Entry<String, Version> entry : missingDeps.entrySet()) { 208 System.out.println(" " + entry.getKey()); 209 } 210 211 if (!Kepler.getForceOpen()) { 212 if (Kepler.getRunWithGUI()) { 213 ImportModuleDependenciesAction action = new ImportModuleDependenciesAction( 214 new TableauFrame()); 215 action.setArchiveFile(file); 216 ImportModuleDependenciesAction.ImportChoice choice = action.checkDependencies(); 217 if (choice == ImportModuleDependenciesAction.ImportChoice.DO_NOTHING) { 218 _exit(); 219 return; 220 } else 221 if (choice == ImportModuleDependenciesAction.ImportChoice.DOWNLOADING_AND_RESTARTING) { 222 action.waitForDownloadAndRestart(); 223 } 224 } else { 225 System.out.println("ERROR: Cannot execute due to missing dependencies. " 226 + "Either add missing modules or run with -force."); 227 _exit(); 228 return; 229 } 230 } 231 } else { 232 MessageHandler.error("ERROR: this KAR cannot be opened."); 233 _exit(); 234 return; 235 } 236 } 237 238 // For each Actor in the KAR open the MOML 239 for (KAREntry entry : karf.karEntries()) { 240 KeplerLSID lsid = entry.getLSID(); 241 if (isDebugging) { 242 log.debug("Processing entry, LSID=" + lsid + ", type=" + entry.getType()); 243 } 244 if (!ActorMetadataKAREntryHandler.handlesClass(entry.getType())) { 245 if (isDebugging) { 246 log.debug("Opening entry, LSID=" + lsid + ", type=" + entry.getType()); 247 } 248 // WARNING - using null TableauFrame here 249 karf.open(entry, null); 250 continue; 251 } 252 253 // extract MOML to temp file 254 File tmpFile = File.createTempFile("moml", ".xml"); 255 tmpFile.deleteOnExit(); 256 try(InputStream inputStream = karf.getInputStream(entry)) { 257 FileUtils.copyInputStreamToFile(inputStream, tmpFile); 258 } 259 260 // _openModel here so it's found later. This avoids 261 // dropping 262 // listeners added before the next _openModel call. 263 // see 264 // http://bugzilla.ecoinformatics.org/show_bug.cgi?id=5434 265 URL url = specToURL(tmpFile.toString()); 266 if (_configuration != null) { 267 ModelDirectory directory = (ModelDirectory) _configuration.getEntity("directory"); 268 if (directory == null) { 269 throw new InternalErrorException("No model directory!"); 270 } 271 _openModel(url, url, url.toString()); 272 } 273 274 // set for MOML application to run the workflow 275 args[args.length - 1] = tmpFile.getAbsolutePath(); 276 System.out.println("Running workflow " + tmpFile.getAbsolutePath() + " extracted from kar file " 277 + karFileName); 278 } 279 } finally { 280 if (karf != null) { 281 karf.close(); 282 } 283 } 284 } else { 285 // the file is not a KAR, so try to read it directly. 286 _readFile(fileName); 287 } 288 289 // set any parameters in the workflow that were specified on the 290 // command-line. 291 _setParameters(); 292 293 if (_configuration == null) { 294 throw new IllegalActionException("No configuration provided."); 295 } 296 297 // show the configuration after we've loaded the workflow 298 _configuration.showAll(); 299 300 // set additional parameters in the configuration 301 302 if (_run) { 303 /* 304 * FIXME these values not used StringAttribute sa = 305 * (StringAttribute) _configuration .getAttribute("_server"); if 306 * (sa != null) { String value = sa.getExpression(); 307 * System.out.println("Running in server mode: " + true); } 308 * 309 * sa = (StringAttribute) _configuration 310 * .getAttribute("_repository"); if (sa != null) { String value 311 * = sa.getExpression(); System.out.println( 312 * "Default save repository is set to: " + _repository); } 313 */ 314 315 if (_printPDF) { 316 // Need to set background 317 PtolemyPreferences preferences = PtolemyPreferences 318 .getPtolemyPreferencesWithinConfiguration(_configuration); 319 preferences.backgroundColor.setExpression("{1.0, 1.0, 1.0, 1.0}"); 320 } 321 322 // run the workflow 323 runModels(); 324 325 if (_exit) { 326 _exit(); 327 } 328 } else { 329 if (_printPDF) { 330 _printPDF(); 331 } 332 } 333 334 } catch (Throwable ex) { 335 // Make sure that we do not eat the exception if there are 336 // problems parsing. For example, "ptolemy -FOO bar bif.xml" 337 // will crash if bar is not a variable. Instead, use 338 // "ptolemy -FOO \"bar\" bif.xml" 339 throwArgsException(ex, args); 340 } 341 } 342 343 /** Start a new thread that calls Kepler.shutdown() and exits the JVM. */ 344 protected void _exit() { 345 // In vergil, this gets called in the 346 // swing thread, which hangs everything 347 // if we call waitForFinish() directly. 348 // So instead, we create a new thread to 349 // do it. 350 Thread waitThread = new Thread() { 351 @Override 352 public void run() { 353 waitForFinish(); 354 if (_printPDF) { 355 try { 356 _printPDF(); 357 } catch (Exception ex) { 358 ex.printStackTrace(); 359 } 360 } 361 Kepler.shutdown(); 362 StringUtilities.exit(0); 363 } 364 }; 365 366 // Note that we start the thread here, which could 367 // be risky when we subclass, since the thread will be 368 // started before the subclass constructor finishes 369 // (FindBugs) 370 waitThread.start(); 371 } 372 373 /** 374 * Parse the command-line arguments. 375 * 376 * @param args 377 * The command-line arguments to be parsed. 378 * @exception Exception 379 * If an argument is not understood or triggers an error. 380 */ 381 protected void parseArgs(String[] args) throws Exception { 382 383 _parameterNames.clear(); 384 _parameterValues.clear(); 385 386 // NOTE: do not try to parse the last argument since that is 387 // the kar file. 388 // Do not parse the first argument since it is the configuration file. 389 for (int i = 1; i < args.length - 1; i++) { 390 String arg = args[i]; 391 392 if (parseArg(arg) == false) { 393 if (arg.trim().startsWith("-")) { 394 if (i >= (args.length - 1)) { 395 throw new IllegalActionException( 396 "Cannot set " + "parameter " + arg + " when no value is " + "given."); 397 } 398 399 // Save in case this is a parameter name and value. 400 _parameterNames.add(arg.substring(1)); 401 _parameterValues.add(args[i + 1]); 402 i++; 403 } else { 404 // Unrecognized option. 405 throw new IllegalActionException("Unrecognized option: " + arg); 406 } 407 } 408 } 409 410 if (_expectingClass) { 411 throw new IllegalActionException("Missing classname."); 412 } 413 } 414 415 /** 416 * Parse a command-line argument. 417 * 418 * @param arg 419 * The command-line argument to be parsed. 420 * @return True if the argument is understood, false otherwise. 421 * @exception Exception 422 * If something goes wrong. 423 */ 424 protected boolean parseArg(String arg) throws Exception { 425 if (arg.equals("-class")) { 426 _expectingClass = true; 427 } else if (arg.equals("-exit")) { 428 _exit = true; 429 } else if (arg.equals("-help")) { 430 System.out.println(_usage()); 431 432 // NOTE: This means the test suites cannot test -help 433 StringUtilities.exit(0); 434 } else if (arg.equals("-printPDF")) { 435 _printPDF = true; 436 } else if (arg.equals("-run")) { 437 _run = true; 438 } else if (arg.equals("-runThenExit")) { 439 _run = true; 440 _exit = true; 441 } else if (arg.equals("-statistics")) { 442 _statistics = true; 443 } else if (arg.equals("-server")) { 444 _server = true; 445 } else if (arg.startsWith("-repository=")) { 446 int equalsIndex = arg.indexOf("="); 447 if (equalsIndex != -1) { 448 _repository = arg.substring(equalsIndex + 1, arg.length()); 449 } else { 450 System.out.println("The -repository argument must be followed by '=<repository'"); 451 StringUtilities.exit(0); 452 } 453 } else if (arg.startsWith("-domain=")) { 454 int equalsIndex = arg.indexOf("="); 455 if (equalsIndex != -1) { 456 _domain = arg.substring(equalsIndex + 1, arg.length()); 457 } else { 458 System.out.println("The -domain argument must be followed by '=<domain>'"); 459 StringUtilities.exit(0); 460 } 461 } else if (arg.startsWith("-username=")) { 462 int equalsIndex = arg.indexOf("="); 463 if (equalsIndex != -1) { 464 _username = arg.substring(equalsIndex + 1, arg.length()); 465 } else { 466 System.out.println("The -username argument must be followed by '=<username>'"); 467 StringUtilities.exit(0); 468 } 469 } else if (arg.startsWith("-password=")) { 470 int equalsIndex = arg.indexOf("="); 471 if (equalsIndex != -1) { 472 _password = arg.substring(equalsIndex + 1, arg.length()); 473 } else { 474 System.out.println("The -password argument must be followed by '=<password>'"); 475 StringUtilities.exit(0); 476 } 477 } else if (arg.equals("-test")) { 478 _test = true; 479 } else if (arg.equals("-version")) { 480 System.out.println("Version " + VersionAttribute.CURRENT_VERSION.getExpression() 481 + ", Build $Id: KeplerConfigurationApplication.java 34591 2017-07-21 16:59:52Z crawl $"); 482 483 // NOTE: This means the test suites cannot test -version 484 StringUtilities.exit(0); 485 } else if (arg.equals("")) { 486 // Ignore blank argument. 487 } else { 488 if (_expectingClass) { 489 // $PTII/bin/ptolemy -class 490 // ptolemy.domains.sdf.demo.Butterfly.Butterfly 491 // Previous argument was -class 492 _expectingClass = false; 493 494 // Create the class. 495 Class<?> newClass = Class.forName(arg); 496 497 // Instantiate the specified class in a new workspace. 498 Workspace workspace = new Workspace(); 499 500 // Workspace workspace = _configuration.workspace(); 501 // Get the constructor that takes a Workspace argument. 502 Class<?>[] argTypes = new Class[1]; 503 argTypes[0] = workspace.getClass(); 504 505 Constructor<?> constructor = newClass.getConstructor(argTypes); 506 507 Object[] args = new Object[1]; 508 args[0] = workspace; 509 510 NamedObj newModel = (NamedObj) constructor.newInstance(args); 511 512 if (_configuration != null) { 513 _openModel(newModel); 514 515 } else { 516 System.err.println("No configuration found."); 517 throw new IllegalActionException(newModel, "No configuration found."); 518 } 519 } else { 520 if (!arg.startsWith("-")) { 521 // Assume the argument is a file name or URL. 522 // Attempt to read it. 523 _readFile(arg); 524 } else { 525 // Argument not recognized. 526 return false; 527 } 528 } 529 } 530 531 return true; 532 } 533 534 /** Attempt to read a file name. */ 535 private void _readFile(String fileName) throws Exception { 536 537 URL inURL; 538 539 try { 540 inURL = specToURL(fileName); 541 } catch (Exception ex) { 542 try { 543 // Create a File and get the URL so that commands 544 // like 545 // $PTII/bin/vergil $PTII/doc/index.htm#in_browser 546 // work. 547 File inFile = new File(fileName); 548 inURL = inFile.toURI().toURL(); 549 } catch (Exception ex2) { 550 // FIXME: This is a fall back for relative 551 // filenames, 552 // I'm not sure if it will ever be called. 553 inURL = new URL(new URL("file://./"), fileName); 554 } 555 } 556 557 // Strangely, the XmlParser does not want as base the 558 // directory containing the file, but rather the 559 // file itself. 560 URL base = inURL; 561 562 // If a configuration has been found, then 563 // defer to it to read the model. Otherwise, 564 // assume the file is an XML file. 565 if (_configuration != null) { 566 ModelDirectory directory = (ModelDirectory) _configuration.getEntity("directory"); 567 568 if (directory == null) { 569 throw new InternalErrorException("No model directory!"); 570 } 571 572 String key = inURL.toExternalForm(); 573 574 _openModel(base, inURL, key); 575 576 } else { 577 578 _parser.reset(); 579 580 } 581 } 582 583 /** 584 * Set any parameters specified by the command-line arguments in the model. 585 */ 586 private void _setParameters() throws IllegalActionException { 587 588 // Check saved options to see whether any is setting an attribute. 589 Iterator<String> names = _parameterNames.iterator(); 590 Iterator<String> values = _parameterValues.iterator(); 591 592 Map<String, String> parameterFileMap = null; 593 594 while (names.hasNext() && values.hasNext()) { 595 String name = names.next(); 596 String value = values.next(); 597 598 boolean match = false; 599 ModelDirectory directory = (ModelDirectory) _configuration.getEntity("directory"); 600 601 if (directory == null) { 602 throw new InternalErrorException("No model directory!"); 603 } 604 605 Iterator<Effigy> effigies = directory.entityList(Effigy.class).iterator(); 606 607 while (effigies.hasNext()) { 608 Effigy effigy = effigies.next(); 609 610 if (effigy instanceof PtolemyEffigy) { 611 NamedObj model = ((PtolemyEffigy) effigy).getModel(); 612 613 match = ParseWorkflowUtil.setModelParameter(model, name, value); 614 615 } 616 } 617 618 if (!match) { 619 // there was no match for this argument. 620 // see if it is the parameter file argument 621 if (name.equals(PARAMETER_FILE_ARG_NAME)) { 622 // parse the parameter file 623 parameterFileMap = ParseWorkflowUtil.parseParameterFile(value); 624 } else { 625 // Unrecognized option. 626 throw new IllegalActionException("Unrecognized option: " + "No parameter exists with name " + name); 627 } 628 } 629 } 630 631 // see if the parameter file argument was specified and the 632 // file was successfully parsed. 633 if (parameterFileMap != null) { 634 // clear the parameter names and values that we've already 635 // set and then set the ones in the parameter file. 636 _parameterNames.clear(); 637 _parameterValues.clear(); 638 for (Map.Entry<String, String> entry : parameterFileMap.entrySet()) { 639 _parameterNames.add(entry.getKey()); 640 _parameterValues.add(entry.getValue()); 641 } 642 if (!_parameterNames.isEmpty()) { 643 _setParameters(); 644 } 645 } 646 } 647 648 /** Print a stack trace of the error. */ 649 @Override 650 public synchronized void executionError(Manager manager, Throwable throwable) { 651 MessageHandler.error("Command failed.", throwable); 652 super.executionError(manager, throwable); 653 } 654 655 /** 656 * main method 657 */ 658 public static void main(String[] args) { 659 try { 660 new KeplerConfigurationApplication(args); 661 } catch (Throwable throwable) { 662 MessageHandler.error("Command failed", throwable); 663 // Be sure to print the stack trace so that 664 // "$PTII/bin/moml -foo" prints something. 665 System.err.print(KernelException.stackTraceToString(throwable)); 666 System.exit(1); 667 } 668 } 669 670 // //////////////////////////////////////////////////////////////////// 671 // // private methods //// 672 673 /** 674 * read xml and set GUI filter based on classes set in xml file 675 * 676 * @throws IllegalActionException 677 */ 678 private static void _setGUIConfiguration(Configuration configuration, String attName) 679 throws IllegalActionException { 680 Attribute displayAtt = configuration.getAttribute(attName); 681 if (displayAtt != null) { 682 String displayValue = ((StringParameter) displayAtt).stringValue(); 683 String[] displayArray = displayValue.split(","); 684 ptolemy.moml.filter.ClassChanges changes = new ptolemy.moml.filter.ClassChanges(); 685 for (String displayClass : displayArray) { 686 changes.put(displayClass, "ptolemy.actor.lib.Discard"); 687 } 688 } 689 } 690 691 // ///////////////////////////////////////////////////////////////// 692 // // private variables //// 693 694 // Flag indicating that the previous argument was -class. 695 private boolean _expectingClass = false; 696 697 // List of parameter names seen on the command line. 698 private List<String> _parameterNames = new LinkedList<String>(); 699 700 // List of parameter values seen on the command line. 701 private List<String> _parameterValues = new LinkedList<String>(); 702 703 // URL from which the configuration was read. 704 // private static URL _initialSpecificationURL; 705 706 private boolean _server = false; 707 708 private String _repository; 709 710 private String _domain; 711 712 private String _username; 713 714 private String _password; 715 716 /** Name of argument for parameter file. */ 717 private static final String PARAMETER_FILE_ARG_NAME = "paramFile"; 718 719}