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