1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tradefed.config; 18 19 import com.android.tradefed.command.CommandScheduler; 20 import com.android.tradefed.command.ICommandScheduler; 21 import com.android.tradefed.config.gcs.GCSConfigurationFactory; 22 import com.android.tradefed.device.DeviceManager; 23 import com.android.tradefed.device.DeviceSelectionOptions; 24 import com.android.tradefed.device.IDeviceManager; 25 import com.android.tradefed.device.IDeviceMonitor; 26 import com.android.tradefed.device.IDeviceSelection; 27 import com.android.tradefed.device.IMultiDeviceRecovery; 28 import com.android.tradefed.host.HostOptions; 29 import com.android.tradefed.host.IHostOptions; 30 import com.android.tradefed.invoker.shard.IShardHelper; 31 import com.android.tradefed.invoker.shard.StrictShardHelper; 32 import com.android.tradefed.log.ITerribleFailureHandler; 33 import com.android.tradefed.util.ArrayUtil; 34 import com.android.tradefed.util.MultiMap; 35 import com.android.tradefed.util.hostmetric.IHostMonitor; 36 import com.android.tradefed.util.keystore.IKeyStoreFactory; 37 import com.android.tradefed.util.keystore.StubKeyStoreFactory; 38 39 import com.google.common.annotations.VisibleForTesting; 40 41 import org.kxml2.io.KXmlSerializer; 42 43 import java.io.File; 44 import java.io.IOException; 45 import java.io.PrintStream; 46 import java.util.ArrayList; 47 import java.util.Arrays; 48 import java.util.Collection; 49 import java.util.HashMap; 50 import java.util.LinkedHashMap; 51 import java.util.List; 52 import java.util.Map; 53 54 /** 55 * An {@link IGlobalConfiguration} implementation that stores the loaded config objects in a map 56 */ 57 public class GlobalConfiguration implements IGlobalConfiguration { 58 // type names for built in configuration objects 59 public static final String DEVICE_MONITOR_TYPE_NAME = "device_monitor"; 60 public static final String HOST_MONITOR_TYPE_NAME = "host_monitor"; 61 public static final String DEVICE_MANAGER_TYPE_NAME = "device_manager"; 62 public static final String WTF_HANDLER_TYPE_NAME = "wtf_handler"; 63 public static final String HOST_OPTIONS_TYPE_NAME = "host_options"; 64 public static final String DEVICE_REQUIREMENTS_TYPE_NAME = "device_requirements"; 65 public static final String SCHEDULER_TYPE_NAME = "command_scheduler"; 66 public static final String MULTI_DEVICE_RECOVERY_TYPE_NAME = "multi_device_recovery"; 67 public static final String KEY_STORE_TYPE_NAME = "key_store"; 68 public static final String SHARDING_STRATEGY_TYPE_NAME = "sharding_strategy"; 69 public static final String GLOBAL_CONFIG_SERVER = "global_config_server"; 70 71 public static final String GLOBAL_CONFIG_VARIABLE = "TF_GLOBAL_CONFIG"; 72 public static final String GLOBAL_CONFIG_SERVER_CONFIG_VARIABLE = 73 "TF_GLOBAL_CONFIG_SERVER_CONFIG"; 74 private static final String GLOBAL_CONFIG_FILENAME = "tf_global_config.xml"; 75 76 private static Map<String, ObjTypeInfo> sObjTypeMap = null; 77 private static IGlobalConfiguration sInstance = null; 78 private static final Object sInstanceLock = new Object(); 79 80 // Empty embedded configuration available by default 81 private static final String DEFAULT_EMPTY_CONFIG_NAME = "empty"; 82 83 // Configurations to be passed to subprocess 84 private static final String[] CONFIGS_FOR_SUBPROCESS_WHITE_LIST = 85 new String[] {KEY_STORE_TYPE_NAME}; 86 87 /** Mapping of config object type name to config objects. */ 88 private Map<String, List<Object>> mConfigMap; 89 private MultiMap<String, String> mOptionMap; 90 private final String mName; 91 private final String mDescription; 92 93 /** 94 * Returns a reference to the singleton {@link GlobalConfiguration} instance for this TF 95 * instance. 96 * 97 * @throws IllegalStateException if {@link #createGlobalConfiguration(String[])} has not 98 * already been called. 99 */ 100 public static IGlobalConfiguration getInstance() { 101 if (sInstance == null) { 102 throw new IllegalStateException("GlobalConfiguration has not yet been initialized!"); 103 } 104 return sInstance; 105 } 106 107 /** 108 * Returns a reference to the singleton {@link DeviceManager} instance for this TF 109 * instance. 110 * 111 * @throws IllegalStateException if {@link #createGlobalConfiguration(String[])} has not 112 * already been called. 113 */ 114 public static IDeviceManager getDeviceManagerInstance() { 115 if (sInstance == null) { 116 throw new IllegalStateException("GlobalConfiguration has not yet been initialized!"); 117 } 118 return sInstance.getDeviceManager(); 119 } 120 121 public static List<IHostMonitor> getHostMonitorInstances() { 122 if (sInstance == null) { 123 throw new IllegalStateException("GlobalConfiguration has not yet been initialized!"); 124 } 125 return sInstance.getHostMonitors(); 126 } 127 128 /** 129 * Sets up the {@link GlobalConfiguration} singleton for this TF instance. Must be called 130 * once and only once, before anything attempts to call {@link #getInstance()} 131 * 132 * @throws IllegalStateException if called more than once 133 */ 134 public static List<String> createGlobalConfiguration(String[] args) 135 throws ConfigurationException { 136 synchronized (sInstanceLock) { 137 if (sInstance != null) { 138 throw new IllegalStateException("GlobalConfiguration is already initialized!"); 139 } 140 List<String> nonGlobalArgs = new ArrayList<String>(args.length); 141 List<String> nonConfigServerArgs = new ArrayList<String>(args.length); 142 IConfigurationServer globalConfigServer = 143 createGlobalConfigServer(args, nonConfigServerArgs); 144 if (globalConfigServer == null) { 145 String path = getGlobalConfigPath(); 146 IConfigurationFactory configFactory = ConfigurationFactory.getInstance(); 147 sInstance = 148 configFactory.createGlobalConfigurationFromArgs( 149 ArrayUtil.buildArray(new String[] {path}, args), nonGlobalArgs); 150 } else { 151 String currentHostConfig = globalConfigServer.getCurrentHostConfig(); 152 IConfigurationFactory configFactory = 153 GCSConfigurationFactory.getInstance(globalConfigServer); 154 sInstance = 155 configFactory.createGlobalConfigurationFromArgs( 156 ArrayUtil.buildArray( 157 new String[] {currentHostConfig}, 158 nonConfigServerArgs.toArray(new String[0])), 159 nonGlobalArgs); 160 } 161 // Validate that madatory options have been set 162 sInstance.validateOptions(); 163 return nonGlobalArgs; 164 } 165 } 166 167 /** 168 * Returns the path to a global config, if one exists, or <code>null</code> if none could be 169 * found. 170 * <p /> 171 * Search locations, in decreasing order of precedence 172 * <ol> 173 * <li><code>$TF_GLOBAL_CONFIG</code> environment variable</li> 174 * <li><code>tf_global_config.xml</code> file in $PWD</li> 175 * <li>(FIXME) <code>tf_global_config.xml</code> file in dir where <code>tradefed.sh</code> 176 * lives</li> 177 * </ol> 178 */ 179 private static String getGlobalConfigPath() { 180 String path = System.getenv(GLOBAL_CONFIG_VARIABLE); 181 if (path != null) { 182 // don't actually check for accessibility here, since the variable might be specifying 183 // a java resource rather than a filename. Even so, this can help the user figure out 184 // which global config (if any) was picked up by TF. 185 System.out.format( 186 "Attempting to use global config \"%s\" from variable $%s.\n", 187 path, GLOBAL_CONFIG_VARIABLE); 188 return path; 189 } 190 191 File file = new File(GLOBAL_CONFIG_FILENAME); 192 if (file.exists()) { 193 path = file.getPath(); 194 System.out.format("Attempting to use autodetected global config \"%s\".\n", path); 195 return path; 196 } 197 198 // FIXME: search in tradefed.sh launch dir (or classpath?) 199 200 // Use default empty known global config 201 return DEFAULT_EMPTY_CONFIG_NAME; 202 } 203 204 /** 205 * Returns an {@link IConfigurationServer}, if one exists, or <code>null</code> if none could be 206 * found. 207 * 208 * @param args for config server 209 * @param nonConfigServerArgs a list which will be populated with the arguments that weren't 210 * processed as global arguments 211 * @return an {@link IConfigurationServer} 212 * @throws ConfigurationException 213 */ 214 @VisibleForTesting 215 static IConfigurationServer createGlobalConfigServer( 216 String[] args, List<String> nonConfigServerArgs) throws ConfigurationException { 217 String path = System.getenv(GLOBAL_CONFIG_SERVER_CONFIG_VARIABLE); 218 if (path == null) { 219 // No config server, should use config files. 220 nonConfigServerArgs.addAll(Arrays.asList(args)); 221 return null; 222 } else { 223 System.out.format("Use global config server config %s.\n", path); 224 } 225 IConfigurationServer configServer = null; 226 IConfigurationFactory configFactory = ConfigurationFactory.getInstance(); 227 IGlobalConfiguration configServerConfig = 228 configFactory.createGlobalConfigurationFromArgs( 229 ArrayUtil.buildArray(new String[] {path}, args), nonConfigServerArgs); 230 configServer = configServerConfig.getGlobalConfigServer(); 231 return configServer; 232 } 233 234 /** 235 * Container struct for built-in config object type 236 */ 237 private static class ObjTypeInfo { 238 final Class<?> mExpectedType; 239 /** true if a list (ie many objects in a single config) are supported for this type */ 240 final boolean mIsListSupported; 241 242 ObjTypeInfo(Class<?> expectedType, boolean isList) { 243 mExpectedType = expectedType; 244 mIsListSupported = isList; 245 } 246 } 247 248 /** 249 * Determine if given config object type name is a built in object 250 * 251 * @param typeName the config object type name 252 * @return <code>true</code> if name is a built in object type 253 */ 254 static boolean isBuiltInObjType(String typeName) { 255 return getObjTypeMap().containsKey(typeName); 256 } 257 258 private static synchronized Map<String, ObjTypeInfo> getObjTypeMap() { 259 if (sObjTypeMap == null) { 260 sObjTypeMap = new HashMap<String, ObjTypeInfo>(); 261 sObjTypeMap.put(HOST_OPTIONS_TYPE_NAME, new ObjTypeInfo(IHostOptions.class, false)); 262 sObjTypeMap.put(DEVICE_MONITOR_TYPE_NAME, new ObjTypeInfo(IDeviceMonitor.class, true)); 263 sObjTypeMap.put(HOST_MONITOR_TYPE_NAME, new ObjTypeInfo(IHostMonitor.class, true)); 264 sObjTypeMap.put(DEVICE_MANAGER_TYPE_NAME, new ObjTypeInfo(IDeviceManager.class, false)); 265 sObjTypeMap.put(DEVICE_REQUIREMENTS_TYPE_NAME, new ObjTypeInfo(IDeviceSelection.class, 266 false)); 267 sObjTypeMap.put(WTF_HANDLER_TYPE_NAME, 268 new ObjTypeInfo(ITerribleFailureHandler.class, false)); 269 sObjTypeMap.put(SCHEDULER_TYPE_NAME, new ObjTypeInfo(ICommandScheduler.class, false)); 270 sObjTypeMap.put( 271 MULTI_DEVICE_RECOVERY_TYPE_NAME, 272 new ObjTypeInfo(IMultiDeviceRecovery.class, true)); 273 sObjTypeMap.put(KEY_STORE_TYPE_NAME, new ObjTypeInfo(IKeyStoreFactory.class, false)); 274 sObjTypeMap.put( 275 SHARDING_STRATEGY_TYPE_NAME, new ObjTypeInfo(IShardHelper.class, false)); 276 sObjTypeMap.put( 277 GLOBAL_CONFIG_SERVER, new ObjTypeInfo(IConfigurationServer.class, false)); 278 } 279 return sObjTypeMap; 280 } 281 282 /** 283 * Creates a {@link GlobalConfiguration} with default config objects 284 */ 285 GlobalConfiguration(String name, String description) { 286 mName = name; 287 mDescription = description; 288 mConfigMap = new LinkedHashMap<String, List<Object>>(); 289 mOptionMap = new MultiMap<String, String>(); 290 setHostOptions(new HostOptions()); 291 setDeviceRequirements(new DeviceSelectionOptions()); 292 setDeviceManager(new DeviceManager()); 293 setCommandScheduler(new CommandScheduler()); 294 setKeyStoreFactory(new StubKeyStoreFactory()); 295 setShardingStrategy(new StrictShardHelper()); 296 } 297 298 /** 299 * @return the name of this {@link Configuration} 300 */ 301 public String getName() { 302 return mName; 303 } 304 305 /** 306 * @return a short user readable description this {@link Configuration} 307 */ 308 public String getDescription() { 309 return mDescription; 310 } 311 312 /** 313 * {@inheritDoc} 314 */ 315 @Override 316 public IHostOptions getHostOptions() { 317 return (IHostOptions) getConfigurationObject(HOST_OPTIONS_TYPE_NAME); 318 } 319 320 /** {@inheritDoc} */ 321 @Override 322 @SuppressWarnings("unchecked") 323 public List<IDeviceMonitor> getDeviceMonitors() { 324 return (List<IDeviceMonitor>) getConfigurationObjectList(DEVICE_MONITOR_TYPE_NAME); 325 } 326 327 @Override 328 public IConfigurationServer getGlobalConfigServer() { 329 return (IConfigurationServer) getConfigurationObject(GLOBAL_CONFIG_SERVER); 330 } 331 332 /** 333 * {@inheritDoc} 334 */ 335 @Override 336 @SuppressWarnings("unchecked") 337 public List<IHostMonitor> getHostMonitors() { 338 return (List<IHostMonitor>) getConfigurationObjectList(HOST_MONITOR_TYPE_NAME); 339 } 340 341 /** 342 * {@inheritDoc} 343 */ 344 @Override 345 public ITerribleFailureHandler getWtfHandler() { 346 return (ITerribleFailureHandler) getConfigurationObject(WTF_HANDLER_TYPE_NAME); 347 } 348 349 /** 350 * {@inheritDoc} 351 */ 352 @Override 353 public IKeyStoreFactory getKeyStoreFactory() { 354 return (IKeyStoreFactory) getConfigurationObject(KEY_STORE_TYPE_NAME); 355 } 356 357 /** {@inheritDoc} */ 358 @Override 359 public IShardHelper getShardingStrategy() { 360 return (IShardHelper) getConfigurationObject(SHARDING_STRATEGY_TYPE_NAME); 361 } 362 363 /** {@inheritDoc} */ 364 @Override 365 public IDeviceManager getDeviceManager() { 366 return (IDeviceManager)getConfigurationObject(DEVICE_MANAGER_TYPE_NAME); 367 } 368 369 /** 370 * {@inheritDoc} 371 */ 372 @Override 373 public IDeviceSelection getDeviceRequirements() { 374 return (IDeviceSelection)getConfigurationObject(DEVICE_REQUIREMENTS_TYPE_NAME); 375 } 376 377 /** 378 * {@inheritDoc} 379 */ 380 @Override 381 public ICommandScheduler getCommandScheduler() { 382 return (ICommandScheduler)getConfigurationObject(SCHEDULER_TYPE_NAME); 383 } 384 385 /** 386 * {@inheritDoc} 387 */ 388 @Override 389 @SuppressWarnings("unchecked") 390 public List<IMultiDeviceRecovery> getMultiDeviceRecoveryHandlers() { 391 return (List<IMultiDeviceRecovery>)getConfigurationObjectList( 392 MULTI_DEVICE_RECOVERY_TYPE_NAME); 393 } 394 395 /** 396 * Internal helper to get the list of config object 397 */ 398 private List<?> getConfigurationObjectList(String typeName) { 399 return mConfigMap.get(typeName); 400 } 401 402 /** 403 * {@inheritDoc} 404 */ 405 @Override 406 public Object getConfigurationObject(String typeName) { 407 List<?> configObjects = getConfigurationObjectList(typeName); 408 if (configObjects == null) { 409 return null; 410 } 411 ObjTypeInfo typeInfo = getObjTypeMap().get(typeName); 412 if (typeInfo != null && typeInfo.mIsListSupported) { 413 throw new IllegalStateException( 414 String.format( 415 "Wrong method call for type %s. Used getConfigurationObject() for a " 416 + "config object that is stored as a list", 417 typeName)); 418 } 419 if (configObjects.size() != 1) { 420 throw new IllegalStateException(String.format( 421 "Attempted to retrieve single object for %s, but %d are present", 422 typeName, configObjects.size())); 423 } 424 return configObjects.get(0); 425 } 426 427 /** 428 * Return a copy of all config objects 429 */ 430 private Collection<Object> getAllConfigurationObjects() { 431 Collection<Object> objectsCopy = new ArrayList<Object>(); 432 for (List<Object> objectList : mConfigMap.values()) { 433 objectsCopy.addAll(objectList); 434 } 435 return objectsCopy; 436 } 437 438 /** 439 * {@inheritDoc} 440 */ 441 @Override 442 public void injectOptionValue(String optionName, String optionValue) 443 throws ConfigurationException { 444 injectOptionValue(optionName, null, optionValue); 445 } 446 447 /** 448 * {@inheritDoc} 449 */ 450 @Override 451 public void injectOptionValue(String optionName, String optionKey, String optionValue) 452 throws ConfigurationException { 453 OptionSetter optionSetter = new OptionSetter(getAllConfigurationObjects()); 454 optionSetter.setOptionValue(optionName, optionKey, optionValue); 455 456 if (optionKey != null) { 457 mOptionMap.put(optionName, optionKey + "=" + optionValue); 458 } else { 459 mOptionMap.put(optionName, optionValue); 460 } 461 } 462 463 /** 464 * {@inheritDoc} 465 */ 466 @Override 467 public List<String> getOptionValues(String optionName) { 468 return mOptionMap.get(optionName); 469 } 470 471 /** 472 * {@inheritDoc} 473 */ 474 @Override 475 public void setHostOptions(IHostOptions hostOptions) { 476 setConfigurationObjectNoThrow(HOST_OPTIONS_TYPE_NAME, hostOptions); 477 } 478 479 /** 480 * {@inheritDoc} 481 */ 482 @Override 483 public void setDeviceMonitor(IDeviceMonitor monitor) { 484 setConfigurationObjectNoThrow(DEVICE_MONITOR_TYPE_NAME, monitor); 485 } 486 487 /** {@inheritDoc} */ 488 @Override 489 public void setHostMonitors(List<IHostMonitor> hostMonitors) { 490 setConfigurationObjectListNoThrow(HOST_MONITOR_TYPE_NAME, hostMonitors); 491 } 492 493 /** 494 * {@inheritDoc} 495 */ 496 @Override 497 public void setWtfHandler(ITerribleFailureHandler wtfHandler) { 498 setConfigurationObjectNoThrow(WTF_HANDLER_TYPE_NAME, wtfHandler); 499 } 500 501 /** 502 * {@inheritDoc} 503 */ 504 @Override 505 public void setKeyStoreFactory(IKeyStoreFactory factory) { 506 setConfigurationObjectNoThrow(KEY_STORE_TYPE_NAME, factory); 507 } 508 509 /** {@inheritDoc} */ 510 @Override 511 public void setShardingStrategy(IShardHelper sharding) { 512 setConfigurationObjectNoThrow(SHARDING_STRATEGY_TYPE_NAME, sharding); 513 } 514 515 /** {@inheritDoc} */ 516 @Override 517 public void setDeviceManager(IDeviceManager manager) { 518 setConfigurationObjectNoThrow(DEVICE_MANAGER_TYPE_NAME, manager); 519 } 520 521 /** 522 * {@inheritDoc} 523 */ 524 @Override 525 public void setDeviceRequirements(IDeviceSelection devRequirements) { 526 setConfigurationObjectNoThrow(DEVICE_REQUIREMENTS_TYPE_NAME, devRequirements); 527 } 528 529 /** 530 * {@inheritDoc} 531 */ 532 @Override 533 public void setCommandScheduler(ICommandScheduler scheduler) { 534 setConfigurationObjectNoThrow(SCHEDULER_TYPE_NAME, scheduler); 535 } 536 537 /** 538 * {@inheritDoc} 539 */ 540 @Override 541 public void setConfigurationObject(String typeName, Object configObject) 542 throws ConfigurationException { 543 if (configObject == null) { 544 throw new IllegalArgumentException("configObject cannot be null"); 545 } 546 mConfigMap.remove(typeName); 547 addObject(typeName, configObject); 548 } 549 550 /** 551 * {@inheritDoc} 552 */ 553 @Override 554 public void setConfigurationObjectList(String typeName, List<?> configList) 555 throws ConfigurationException { 556 if (configList == null) { 557 throw new IllegalArgumentException("configList cannot be null"); 558 } 559 mConfigMap.remove(typeName); 560 for (Object configObject : configList) { 561 addObject(typeName, configObject); 562 } 563 } 564 565 /** 566 * A wrapper around {@link #setConfigurationObjectList(String, List)} that will not throw {@link 567 * ConfigurationException}. 568 * 569 * <p>Intended to be used in cases where its guaranteed that <var>configObject</var> is the 570 * correct type 571 */ 572 private void setConfigurationObjectListNoThrow(String typeName, List<?> configList) { 573 try { 574 setConfigurationObjectList(typeName, configList); 575 } catch (ConfigurationException e) { 576 // should never happen 577 throw new IllegalArgumentException(e); 578 } 579 } 580 581 /** 582 * Adds a loaded object to this configuration. 583 * 584 * @param typeName the unique object type name of the configuration object 585 * @param configObject the configuration object 586 * @throws ConfigurationException if object was not the correct type 587 */ 588 private void addObject(String typeName, Object configObject) throws ConfigurationException { 589 List<Object> objList = mConfigMap.get(typeName); 590 if (objList == null) { 591 objList = new ArrayList<Object>(1); 592 mConfigMap.put(typeName, objList); 593 } 594 ObjTypeInfo typeInfo = getObjTypeMap().get(typeName); 595 if (typeInfo != null && !typeInfo.mExpectedType.isInstance(configObject)) { 596 throw new ConfigurationException(String.format( 597 "The config object %s is not the correct type. Expected %s, received %s", 598 typeName, typeInfo.mExpectedType.getCanonicalName(), 599 configObject.getClass().getCanonicalName())); 600 } 601 if (typeInfo != null && !typeInfo.mIsListSupported && objList.size() > 0) { 602 throw new ConfigurationException(String.format( 603 "Only one config object allowed for %s, but multiple were specified.", 604 typeName)); 605 } 606 objList.add(configObject); 607 } 608 609 /** 610 * A wrapper around {@link #setConfigurationObject(String, Object)} that will not throw 611 * {@link ConfigurationException}. 612 * <p/> 613 * Intended to be used in cases where its guaranteed that <var>configObject</var> is the 614 * correct type. 615 * 616 * @param typeName 617 * @param configObject 618 */ 619 private void setConfigurationObjectNoThrow(String typeName, Object configObject) { 620 try { 621 setConfigurationObject(typeName, configObject); 622 } catch (ConfigurationException e) { 623 // should never happen 624 throw new IllegalArgumentException(e); 625 } 626 } 627 628 /** 629 * {@inheritDoc} 630 */ 631 @Override 632 public List<String> setOptionsFromCommandLineArgs(List<String> listArgs) 633 throws ConfigurationException { 634 ArgsOptionParser parser = new ArgsOptionParser(getAllConfigurationObjects()); 635 return parser.parse(listArgs); 636 } 637 638 /** 639 * Outputs a command line usage help text for this configuration to given printStream. 640 * 641 * @param out the {@link PrintStream} to use. 642 * @throws ConfigurationException 643 */ 644 public void printCommandUsage(boolean importantOnly, PrintStream out) 645 throws ConfigurationException { 646 out.println(String.format("'%s' configuration: %s", getName(), getDescription())); 647 out.println(); 648 if (importantOnly) { 649 out.println("Printing help for only the important options. " + 650 "To see help for all options, use the --help-all flag"); 651 out.println(); 652 } 653 for (Map.Entry<String, List<Object>> configObjectsEntry : mConfigMap.entrySet()) { 654 for (Object configObject : configObjectsEntry.getValue()) { 655 String optionHelp = printOptionsForObject(importantOnly, 656 configObjectsEntry.getKey(), configObject); 657 // only print help for object if optionHelp is non zero length 658 if (optionHelp.length() > 0) { 659 String classAlias = ""; 660 if (configObject.getClass().isAnnotationPresent(OptionClass.class)) { 661 final OptionClass classAnnotation = configObject.getClass().getAnnotation( 662 OptionClass.class); 663 classAlias = String.format("'%s' ", classAnnotation.alias()); 664 } 665 out.printf(" %s%s options:", classAlias, configObjectsEntry.getKey()); 666 out.println(); 667 out.print(optionHelp); 668 out.println(); 669 } 670 } 671 } 672 } 673 674 /** 675 * Prints out the available config options for given configuration object. 676 * 677 * @param importantOnly print only the important options 678 * @param objectTypeName the config object type name. Used to generate more descriptive error 679 * messages 680 * @param configObject the config object 681 * @return a {@link String} of option help text 682 * @throws ConfigurationException 683 */ 684 private String printOptionsForObject(boolean importantOnly, String objectTypeName, 685 Object configObject) throws ConfigurationException { 686 return ArgsOptionParser.getOptionHelp(importantOnly, configObject); 687 } 688 689 /** 690 * {@inheritDoc} 691 */ 692 @Override 693 public void validateOptions() throws ConfigurationException { 694 new ArgsOptionParser(getAllConfigurationObjects()).validateMandatoryOptions(); 695 } 696 697 /** {@inheritDoc} */ 698 @Override 699 public void cloneConfigWithFilter(File outputXml, String[] whitelistConfigs) 700 throws IOException { 701 KXmlSerializer serializer = ConfigurationUtil.createSerializer(outputXml); 702 serializer.startTag(null, ConfigurationUtil.CONFIGURATION_NAME); 703 if (whitelistConfigs == null) { 704 whitelistConfigs = CONFIGS_FOR_SUBPROCESS_WHITE_LIST; 705 } 706 for (String config : whitelistConfigs) { 707 ConfigurationUtil.dumpClassToXml( 708 serializer, config, getConfigurationObject(config), new ArrayList<>()); 709 } 710 serializer.endTag(null, ConfigurationUtil.CONFIGURATION_NAME); 711 serializer.endDocument(); 712 } 713 } 714