Home | History | Annotate | Download | only in config
      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