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