001/* 002 * #%L 003 * Netarchivesuite - deploy 004 * %% 005 * Copyright (C) 2005 - 2014 The Royal Danish Library, the Danish State and University Library, 006 * the National Library of France and the Austrian National Library. 007 * %% 008 * This program is free software: you can redistribute it and/or modify 009 * it under the terms of the GNU Lesser General Public License as 010 * published by the Free Software Foundation, either version 2.1 of the 011 * License, or (at your option) any later version. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Lesser Public License for more details. 017 * 018 * You should have received a copy of the GNU General Lesser Public 019 * License along with this program. If not, see 020 * <http://www.gnu.org/licenses/lgpl-2.1.html>. 021 * #L% 022 */ 023package dk.netarkivet.deploy; 024 025import java.io.File; 026import java.io.IOException; 027import java.nio.charset.Charset; 028import java.util.Optional; 029 030import org.apache.commons.cli.CommandLine; 031import org.apache.commons.cli.CommandLineParser; 032import org.apache.commons.cli.Option; 033import org.apache.commons.cli.Options; 034import org.apache.commons.cli.ParseException; 035import org.apache.commons.cli.PosixParser; 036 037import dk.netarkivet.common.utils.Settings; 038 039/** 040 * The application that is run to generate install and start/stop scripts for all physical locations, machines and 041 * applications. 042 * <p> 043 * The actual deployment has to be done from an Linux/Unix machine, and this application should therefore not be run on 044 * Windows. 045 */ 046public final class DeployApplication { 047 048 static { 049 Settings.addDefaultClasspathSettings(Constants.BUILD_COMPLETE_SETTINGS_FILE_PATH); 050 } 051 052 /** The configuration for this deploy. */ 053 private static DeployConfiguration deployConfig; 054 /** Argument parameter. */ 055 private static ArgumentParameters ap = new ArgumentParameters(); 056 /** The deploy-config file. */ 057 private static File deployConfigFile; 058 /** The NetarchiveSuite file. */ 059 private static File netarchiveSuiteFile; 060 /** The security policy file. */ 061 private static File secPolicyFile; 062 /** SLF4J xml configuration file. */ 063 private static File slf4jConfigFile; 064 /** The database file. */ 065 private static File dbFile; 066 /** The arguments for resetting tempDir. */ 067 private static boolean resetDirectory; 068 /** The archive database file. */ 069 private static File arcDbFile; 070 /** The folder with the external libraries to be deployed. */ 071 private static File externalJarFolder; 072 private static Optional<File> defaultBundlerZip; 073 074 /** 075 * Private constructor to disallow instantiation of this class. 076 */ 077 private DeployApplication() { 078 } 079 080 /** 081 * Run deploy. 082 * 083 * @param args The Command-line arguments in no particular order: 084 * 085 * -C The deploy configuration file (ends with .xml). -Z The NetarchiveSuite file to be unpacked (ends with .zip). 086 * -S The security policy file (ends with .policy). -L The logging property file (ends with .prop). -O [OPTIONAL] 087 * The output directory -D [OPTIONAL] The harvest definition database -T [OPTIONAL] The test arguments 088 * (httpportoffset, port, environmentName, mailReceiver) -R [OPTIONAL] For resetting the tempDir (takes arguments 089 * 'y' or 'yes') -E [OPTIONAL] Evaluating the deployConfig file (arguments: 'y' or 'yes') -A [OPTIONAL] For archive 090 * database. -J [OPTIONAL] For deploying with external jar files. Must be the total path to the directory containing 091 * jar-files. These external files will be placed on every machine, and they have to manually be put into the 092 * classpath, where they should be used. 093 */ 094 public static void main(String[] args) { 095 try { 096 // Make sure the arguments can be parsed. 097 if (!ap.parseParameters(args)) { 098 System.err.print(Constants.MSG_ERROR_PARSE_ARGUMENTS); 099 System.out.println(ap.listArguments()); 100 System.exit(1); 101 } 102 103 // Check arguments 104 if (ap.getCommandLine().getOptions().length < Constants.ARGUMENTS_REQUIRED) { 105 System.err.print(Constants.MSG_ERROR_NOT_ENOUGH_ARGUMENTS); 106 System.out.println(); 107 System.out.println("Use DeployApplication with following arguments:"); 108 System.out.println(ap.listArguments()); 109 System.out.println("outputdir defaults to ./environmentName (set in config file)"); 110 System.exit(1); 111 } 112 // test if more arguments than options is given 113 if (args.length > ap.getOptions().getOptions().size()) { 114 System.err.print(Constants.MSG_ERROR_TOO_MANY_ARGUMENTS); 115 System.out.println(); 116 System.out.println("Maximum " + ap.getOptions().getOptions().size() + "arguments."); 117 System.exit(1); 118 } 119 120 // Retrieving the configuration filename 121 String deployConfigFileName = ap.getCommandLine().getOptionValue(Constants.ARG_CONFIG_FILE); 122 // Retrieving the NetarchiveSuite filename 123 String netarchiveSuiteFileName = ap.getCommandLine().getOptionValue(Constants.ARG_NETARCHIVE_SUITE_FILE); 124 // Retrieving the security policy filename 125 String secPolicyFileName = ap.getCommandLine().getOptionValue(Constants.ARG_SECURITY_FILE); 126 // Retrieving the SLF4J xml filename 127 String slf4jConfigFileName = ap.getCommandLine().getOptionValue(Constants.ARG_SLF4J_CONFIG_FILE); 128 // Retrieving the output directory name 129 String outputDir = ap.getCommandLine().getOptionValue(Constants.ARG_OUTPUT_DIRECTORY); 130 // Retrieving the database filename 131 String databaseFileName = ap.getCommandLine().getOptionValue(Constants.ARG_DATABASE_FILE); 132 // Retrieving the test arguments 133 String testArguments = ap.getCommandLine().getOptionValue(Constants.ARG_TEST); 134 // Retrieving the reset argument 135 String resetArgument = ap.getCommandLine().getOptionValue(Constants.ARG_RESET); 136 // Retrieving the evaluate argument 137 String evaluateArgument = ap.getCommandLine().getOptionValue(Constants.ARG_EVALUATE); 138 // Retrieve the archive database filename. 139 String arcDbFileName = ap.getCommandLine().getOptionValue(Constants.ARG_ARC_DB); 140 // Retrieves the jar-folder name. 141 String jarFolderName = ap.getCommandLine().getOptionValue(Constants.ARG_JAR_FOLDER); 142 143 // Retrieves the source encoding. 144 // If not specified get system default 145 String sourceEncoding = ap.getCommandLine().getOptionValue(Constants.ARG_SOURCE_ENCODING); 146 String msgTail = ""; 147 if (sourceEncoding == null || sourceEncoding.isEmpty()) { 148 sourceEncoding = Charset.defaultCharset().name(); 149 msgTail = " (defaulted)"; 150 } 151 System.out.println("Will read source files using encoding '" + sourceEncoding + "'" + msgTail); 152 153 // check deployConfigFileName and retrieve the corresponding file 154 initConfigFile(deployConfigFileName); 155 156 // check netarchiveSuiteFileName and retrieve the corresponding file 157 initNetarchiveSuiteFile(netarchiveSuiteFileName); 158 159 // check secPolicyFileName and retrieve the corresponding file 160 initSecPolicyFile(secPolicyFileName); 161 162 initSLF4JXmlFile(slf4jConfigFileName); 163 164 // check database 165 initDatabase(databaseFileName); 166 167 // check and apply the test arguments 168 initTestArguments(testArguments); 169 170 // check reset arguments. 171 initReset(resetArgument); 172 173 // evaluates the config file 174 initEvaluate(evaluateArgument, sourceEncoding); 175 176 // check the archive database 177 initArchiveDatabase(arcDbFileName); 178 179 // check the external jar-files library folder. 180 initJarFolder(jarFolderName); 181 182 initBundlerZip(Optional.ofNullable( 183 ap.getCommandLine().getOptionValue(Constants.ARG_DEFAULT_BUNDLER_ZIP))); 184 185 // Make the configuration based on the input data 186 deployConfig = new DeployConfiguration(deployConfigFile, netarchiveSuiteFile, secPolicyFile, 187 slf4jConfigFile, outputDir, dbFile, arcDbFile, resetDirectory, externalJarFolder, sourceEncoding, 188 defaultBundlerZip); 189 190 // Write the scripts, directories and everything 191 deployConfig.write(); 192 } catch (SecurityException e) { 193 // This problem should only occur in tests -> thus not err message. 194 System.out.println("SECURITY ERROR: "); 195 e.printStackTrace(); 196 } catch (Throwable e) { 197 System.err.println("DEPLOY APPLICATION ERROR: "); 198 e.printStackTrace(); 199 } 200 } 201 202 /** 203 * Checks the configuration file argument and retrieves the file. 204 * 205 * @param deployConfigFileName The configuration file argument. 206 */ 207 private static void initConfigFile(String deployConfigFileName) { 208 // check whether deploy-config file name is given as argument 209 if (deployConfigFileName == null) { 210 System.err.print(Constants.MSG_ERROR_NO_CONFIG_FILE_ARG); 211 System.out.println(); 212 System.exit(1); 213 } 214 // check whether deploy-config file has correct extensions 215 if (!deployConfigFileName.endsWith(Constants.EXTENSION_XML_FILES)) { 216 System.err.print(Constants.MSG_ERROR_CONFIG_EXTENSION); 217 System.out.println(); 218 System.exit(1); 219 } 220 // get the file 221 deployConfigFile = new File(deployConfigFileName); 222 // check whether the deploy-config file exists. 223 if (!deployConfigFile.exists()) { 224 System.err.print(Constants.MSG_ERROR_NO_CONFIG_FILE_FOUND); 225 System.out.println(); 226 System.exit(1); 227 } 228 } 229 230 /** 231 * Checks the NetarchiveSuite file argument and retrieves the file. 232 * 233 * @param netarchiveSuiteFileName The NetarchiveSuite argument. 234 */ 235 private static void initNetarchiveSuiteFile(String netarchiveSuiteFileName) { 236 // check whether NetarchiveSuite file name is given as argument 237 if (netarchiveSuiteFileName == null) { 238 System.err.print(Constants.MSG_ERROR_NO_NETARCHIVESUITE_FILE_ARG); 239 System.out.println(); 240 System.exit(1); 241 } 242 // check whether the NetarchiveSuite file has correct extensions 243 if (!netarchiveSuiteFileName.endsWith(Constants.EXTENSION_ZIP_FILES)) { 244 System.err.print(Constants.MSG_ERROR_NETARCHIVESUITE_EXTENSION); 245 System.out.println(); 246 System.exit(1); 247 } 248 // get the file 249 netarchiveSuiteFile = new File(netarchiveSuiteFileName); 250 // check whether the NetarchiveSuite file exists. 251 if (!netarchiveSuiteFile.isFile()) { 252 System.err.print(Constants.MSG_ERROR_NO_NETARCHIVESUITE_FILE_FOUND); 253 System.out.println(); 254 System.out.println("Couldn't find file: " + netarchiveSuiteFile.getAbsolutePath()); 255 System.exit(1); 256 } 257 } 258 259 /** 260 * Checks the security policy file argument and retrieves the file. 261 * 262 * @param secPolicyFileName The security policy argument. 263 */ 264 private static void initSecPolicyFile(String secPolicyFileName) { 265 // check whether security policy file name is given as argument 266 if (secPolicyFileName == null) { 267 System.err.print(Constants.MSG_ERROR_NO_SECURITY_FILE_ARG); 268 System.out.println(); 269 System.exit(1); 270 } 271 // check whether security policy file has correct extensions 272 if (!secPolicyFileName.endsWith(Constants.EXTENSION_POLICY_FILES)) { 273 System.err.print(Constants.MSG_ERROR_SECURITY_EXTENSION); 274 System.out.println(); 275 System.exit(1); 276 } 277 // get the file 278 secPolicyFile = new File(secPolicyFileName); 279 // check whether the security policy file exists. 280 if (!secPolicyFile.exists()) { 281 System.err.print(Constants.MSG_ERROR_NO_SECURITY_FILE_FOUND); 282 System.out.println(); 283 System.out.println("Couldn't find file: " + secPolicyFile.getAbsolutePath()); 284 System.exit(1); 285 } 286 } 287 288 /** 289 * Checks the SLF4J config file argument and retrieves the file. 290 * 291 * @param slf4jXmlFileName The SLF4J config argument. 292 */ 293 private static void initSLF4JXmlFile(String slf4jXmlFileName) { 294 // check whether SLF4J config file name is given as argument 295 if (slf4jXmlFileName == null) { 296 System.err.print(Constants.MSG_ERROR_NO_SLF4J_CONFIG_FILE_ARG); 297 System.out.println(); 298 System.exit(1); 299 } 300 // check whether the SLF4J xml file has correct extensions 301 if (!slf4jXmlFileName.endsWith(Constants.EXTENSION_XML_FILES)) { 302 System.err.print(Constants.MSG_ERROR_SLF4J_CONFIG_EXTENSION); 303 System.out.println(); 304 System.exit(1); 305 } 306 // get the file 307 slf4jConfigFile = new File(slf4jXmlFileName); 308 // check whether the SLF4J xml file exists. 309 if (!slf4jConfigFile.exists()) { 310 System.err.print(Constants.MSG_ERROR_NO_SLF4J_CONFIG_FILE_FOUND); 311 System.out.println(); 312 System.out.println("Couldn't find file: " + slf4jConfigFile.getAbsolutePath()); 313 System.exit(1); 314 } 315 } 316 317 /** 318 * Checks the database argument (if any) for extension and existence. 319 * 320 * @param databaseFileName The name of the database file. 321 */ 322 private static void initDatabase(String databaseFileName) { 323 dbFile = null; 324 // check the extension on the database, if it is given as argument 325 if (databaseFileName != null) { 326 if (!databaseFileName.endsWith(Constants.EXTENSION_JAR_FILES) 327 && !databaseFileName.endsWith(Constants.EXTENSION_ZIP_FILES)) { 328 System.err.print(Constants.MSG_ERROR_DATABASE_EXTENSION); 329 System.out.println(); 330 System.exit(1); 331 } 332 333 // get the file 334 dbFile = new File(databaseFileName); 335 // check whether the database file exists. 336 if (!dbFile.isFile()) { 337 System.err.print(Constants.MSG_ERROR_NO_DATABASE_FILE_FOUND); 338 System.out.println(); 339 System.out.println("Couldn't find file: " + dbFile.getAbsolutePath()); 340 System.exit(1); 341 } 342 } 343 } 344 345 /** 346 * Checks the arguments for resetting the directory. Only the arguments 'y' or 'yes' resets the database directory. 347 * Default is 'no'. 348 * <p> 349 * If another argument than 'y', 'yes', 'n' or 'no' is given, an warning is given. 350 * 351 * @param resetArgument The argument for resetting given. 352 */ 353 private static void initReset(String resetArgument) { 354 if (resetArgument != null) { 355 if (resetArgument.equalsIgnoreCase(Constants.YES_SHORT) 356 || resetArgument.equalsIgnoreCase(Constants.YES_LONG)) { 357 // if positive argument, then set to true. 358 resetDirectory = true; 359 } else if (resetArgument.equalsIgnoreCase(Constants.NO_SHORT) 360 || resetArgument.equalsIgnoreCase(Constants.NO_LONG)) { 361 // if negative argument, then set to false. 362 resetDirectory = false; 363 } else { 364 // if wrong argument, notify and set to false. 365 System.err.println(Constants.MSG_ERROR_RESET_ARGUMENT); 366 resetDirectory = false; 367 } 368 } else { 369 // if no arguments, then 370 resetDirectory = false; 371 } 372 } 373 374 /** 375 * Checks the arguments for evaluating the config file. Only the arguments 'y' or 'yes' is accepted for evaluation. 376 * Anything else (including argument set to null) does not evaluate the deployConfigFile. 377 * 378 * @param evaluateArgument The argument for evaluation. 379 * @param encoding the encoding to use to read from the input file 380 */ 381 public static void initEvaluate(String evaluateArgument, String encoding) { 382 // check if argument is given and it is acknowledgement ('y' or 'yes') 383 if ((evaluateArgument != null) 384 && (!evaluateArgument.isEmpty()) 385 && (evaluateArgument.equalsIgnoreCase(Constants.YES_SHORT) || evaluateArgument 386 .equalsIgnoreCase(Constants.YES_LONG))) { 387 // if yes, then evaluate config file 388 EvaluateConfigFile evf = new EvaluateConfigFile(deployConfigFile, encoding); 389 evf.evaluate(); 390 } 391 } 392 393 /** 394 * Checks the argument for the archive database. 395 * 396 * @param arcDbFileName The path to the archive database. 397 */ 398 public static void initArchiveDatabase(String arcDbFileName) { 399 arcDbFile = null; 400 // check the extension on the database, if it is given as argument 401 if (arcDbFileName != null) { 402 if (!arcDbFileName.endsWith(Constants.EXTENSION_JAR_FILES) 403 && !arcDbFileName.endsWith(Constants.EXTENSION_ZIP_FILES)) { 404 System.err.print(Constants.MSG_ERROR_BPDB_EXTENSION); 405 System.out.println(); 406 System.exit(1); 407 } 408 409 // get the file 410 arcDbFile = new File(arcDbFileName); 411 // check whether the database file exists. 412 if (!arcDbFile.isFile()) { 413 System.err.print(Constants.MSG_ERROR_NO_BPDB_FILE_FOUND); 414 System.out.println(); 415 System.out.println("Couldn't find file: " + arcDbFile.getAbsolutePath()); 416 System.exit(1); 417 } 418 } 419 } 420 421 /** 422 * Checks the argument for the external jar-folder. 423 * 424 * @param folderName The path to the folder. Global path. 425 */ 426 public static void initJarFolder(String folderName) { 427 externalJarFolder = null; 428 if (folderName != null && !folderName.isEmpty()) { 429 externalJarFolder = new File(folderName); 430 431 if (!externalJarFolder.isDirectory()) { 432 System.err.print(Constants.MSG_ERROR_NO_JAR_FOLDER); 433 System.out.println(); 434 System.out.println("Couldn't find directory: " + externalJarFolder.getAbsolutePath()); 435 System.exit(1); 436 } 437 } 438 } 439 440 /** 441 * Checks if the default bundler zip file exists if defined. 442 * 443 * @param defaultBundlerZipName The path to the default bundler zip file to use. 444 */ 445 public static void initBundlerZip(Optional<String> defaultBundlerZipName) { 446 if (defaultBundlerZipName.isPresent()) { 447 defaultBundlerZip = Optional.of(new File(defaultBundlerZipName.get())); 448 449 if (!defaultBundlerZip.get().exists()) { 450 System.err.print(Constants.MSG_ERROR_NO_BUNDLER_ZIP_FILE); 451 System.out.println(); 452 System.out.println("Couldn't find default bundler file: " + defaultBundlerZip.get().getAbsolutePath()); 453 System.exit(1); 454 } 455 } else { 456 defaultBundlerZip = Optional.empty(); 457 } 458 } 459 460 /** 461 * Applies the test arguments. 462 * <p> 463 * If the test arguments are given correctly, the configuration file is loaded and changed appropriately, then 464 * written to a test configuration file. 465 * <p> 466 * The new test configuration file has the same name as the original configuration file, except ".xml" is replaced 467 * by "_text.xml". Thus "path/config.xml" -> "path/config_test.xml". 468 * 469 * @param testArguments The test arguments. 470 */ 471 private static void initTestArguments(String testArguments) { 472 // test if any test arguments (if none, don't apply, just stop). 473 if (testArguments == null || testArguments.isEmpty()) { 474 return; 475 } 476 477 String[] changes = testArguments.split(Constants.REGEX_COMMA_CHARACTER); 478 if (changes.length != Constants.TEST_ARGUMENTS_REQUIRED) { 479 System.err.print(Constants.MSG_ERROR_TEST_ARGUMENTS); 480 System.out.println(); 481 System.out.println(changes.length + " arguments was given and " + Constants.TEST_ARGUMENTS_REQUIRED 482 + " was expected."); 483 System.out.println("Received: " + testArguments); 484 System.exit(1); 485 } 486 487 try { 488 CreateTestInstance cti = new CreateTestInstance(deployConfigFile); 489 490 // apply the arguments 491 cti.applyTestArguments(changes[Constants.TEST_ARGUMENT_OFFSET_INDEX], 492 changes[Constants.TEST_ARGUMENT_HTTP_INDEX], 493 changes[Constants.TEST_ARGUMENT_ENVIRONMENT_NAME_INDEX], 494 changes[Constants.TEST_ARGUMENT_MAIL_INDEX]); 495 496 // replace ".xml" with "_test.xml" 497 String tmp = deployConfigFile.getPath(); 498 // split this into two ("path/config.xml" = {"path/config", ".xml"}) 499 String[] configFile = tmp.split(Constants.REGEX_DOT_CHARACTER); 500 // take the first part and add test ending 501 // ("path/config" + "_test.xml" = "path/config_test.xml") 502 String nameOfNewConfig = configFile[0] + Constants.TEST_CONFIG_FILE_REPLACE_ENDING; 503 504 // create and use new config file. 505 cti.createConfigurationFile(nameOfNewConfig); 506 deployConfigFile = new File(nameOfNewConfig); 507 } catch (IOException e) { 508 System.out.println("Error in test arguments: " + e); 509 System.exit(1); 510 } 511 } 512 513 /** 514 * Handles the incoming arguments. 515 */ 516 private static class ArgumentParameters { 517 /** Options object for parameters. */ 518 private Options options = new Options(); 519 /** Parser for parsing the command line arguments. */ 520 private CommandLineParser parser = new PosixParser(); 521 /** The command line. */ 522 private CommandLine cmd; 523 /** Whether the options has an argument. */ 524 private static final boolean HAS_ARG = true; 525 526 /** 527 * Initialise options by setting legal parameters for batch jobs. 528 */ 529 ArgumentParameters() { 530 options.addOption(Constants.ARG_CONFIG_FILE, HAS_ARG, "Config file."); 531 options.addOption(Constants.ARG_NETARCHIVE_SUITE_FILE, HAS_ARG, "The NetarchiveSuite package file."); 532 options.addOption(Constants.ARG_SECURITY_FILE, HAS_ARG, "Security property file."); 533 options.addOption(Constants.ARG_SLF4J_CONFIG_FILE, HAS_ARG, "SLF4J config file."); 534 options.addOption(Constants.ARG_OUTPUT_DIRECTORY, HAS_ARG, "[OPTIONAL] output directory."); 535 options.addOption(Constants.ARG_DATABASE_FILE, HAS_ARG, "[OPTIONAL] Database file."); 536 options.addOption(Constants.ARG_TEST, HAS_ARG, "[OPTIONAL] Tests arguments (offset for http port," 537 + " http port, environment name, mail receiver)."); 538 options.addOption(Constants.ARG_RESET, HAS_ARG, "[OPTIONAL] Reset temp directory ('y' or 'yes'" 539 + "means reset, anything else means do not reset." 540 + " Different from 'y', 'yes', 'n' or 'no' gives" + " an error message."); 541 options.addOption(Constants.ARG_EVALUATE, HAS_ARG, "[OPTIONAL] Evaluate the config file."); 542 options.addOption(Constants.ARG_ARC_DB, HAS_ARG, "[OPTIONAL] The archive database file"); 543 options.addOption(Constants.ARG_JAR_FOLDER, HAS_ARG, "[OPTIONAL] Installing the external jar library " 544 + "files within the given folder."); 545 options.addOption(Constants.ARG_SOURCE_ENCODING, HAS_ARG, "[OPTIONAL] Encoding to use for source files."); 546 options.addOption(Constants.ARG_DEFAULT_BUNDLER_ZIP, HAS_ARG, "[OPTIONAL] The bundled harvester to use. " 547 + "If not provided here the bundler needs to be provided in the settings for each (H3) harvester"); 548 } 549 550 /** 551 * Parsing the input arguments. 552 * 553 * @param args The input arguments. 554 * @return Whether it parsed correctly or not. 555 */ 556 Boolean parseParameters(String[] args) { 557 try { 558 // parse the command line arguments 559 cmd = parser.parse(options, args); 560 } catch (ParseException exp) { 561 System.out.println("Parsing error: " + exp); 562 return false; 563 } 564 return true; 565 } 566 567 /** 568 * Get the list of possible arguments with their description. 569 * 570 * @return The list describing the possible arguments. 571 */ 572 String listArguments() { 573 StringBuilder res = new StringBuilder(); 574 res.append(Constants.NEWLINE); 575 res.append(Constants.INIT_ARGUMENTS_LIST); 576 // add options 577 for (Object o : options.getOptions()) { 578 Option op = (Option) o; 579 res.append(Constants.NEWLINE); 580 res.append(Constants.DASH); 581 res.append(op.getOpt()); 582 res.append(Constants.SPACE); 583 res.append(op.getDescription()); 584 } 585 return res.toString(); 586 } 587 588 /** 589 * For retrieving the options. 590 * 591 * @return The options. 592 */ 593 public Options getOptions() { 594 return options; 595 } 596 597 /** 598 * For retrieving the commandLine. 599 * 600 * @return The cmd. 601 */ 602 public CommandLine getCommandLine() { 603 return cmd; 604 } 605 } 606 607}