1 /* 2 * Copyright (C) 2010 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.IBuildProvider; 20 import com.android.tradefed.command.CommandOptions; 21 import com.android.tradefed.command.ICommandOptions; 22 import com.android.tradefed.config.ConfigurationDef.OptionDef; 23 import com.android.tradefed.config.OptionSetter.FieldDef; 24 import com.android.tradefed.device.IDeviceRecovery; 25 import com.android.tradefed.device.IDeviceSelection; 26 import com.android.tradefed.device.TestDeviceOptions; 27 import com.android.tradefed.device.metric.IMetricCollector; 28 import com.android.tradefed.device.metric.target.DeviceSideCollectorSpecification; 29 import com.android.tradefed.log.ILeveledLogOutput; 30 import com.android.tradefed.log.LogUtil.CLog; 31 import com.android.tradefed.log.StdoutLogger; 32 import com.android.tradefed.postprocessor.BasePostProcessor; 33 import com.android.tradefed.postprocessor.IPostProcessor; 34 import com.android.tradefed.result.FileSystemLogSaver; 35 import com.android.tradefed.result.ILogSaver; 36 import com.android.tradefed.result.ITestInvocationListener; 37 import com.android.tradefed.result.TextResultReporter; 38 import com.android.tradefed.sandbox.SandboxOptions; 39 import com.android.tradefed.suite.checker.ISystemStatusChecker; 40 import com.android.tradefed.targetprep.ITargetPreparer; 41 import com.android.tradefed.targetprep.multi.IMultiTargetPreparer; 42 import com.android.tradefed.testtype.IRemoteTest; 43 import com.android.tradefed.testtype.StubTest; 44 import com.android.tradefed.util.FileUtil; 45 import com.android.tradefed.util.MultiMap; 46 import com.android.tradefed.util.QuotationAwareTokenizer; 47 import com.android.tradefed.util.keystore.IKeyStoreClient; 48 49 import com.google.common.base.Joiner; 50 51 import org.json.JSONArray; 52 import org.json.JSONException; 53 import org.json.JSONObject; 54 import org.kxml2.io.KXmlSerializer; 55 56 import java.io.File; 57 import java.io.IOException; 58 import java.io.PrintStream; 59 import java.io.PrintWriter; 60 import java.lang.reflect.Field; 61 import java.lang.reflect.ParameterizedType; 62 import java.lang.reflect.Type; 63 import java.util.ArrayList; 64 import java.util.Collection; 65 import java.util.HashMap; 66 import java.util.HashSet; 67 import java.util.LinkedHashMap; 68 import java.util.List; 69 import java.util.Map; 70 import java.util.Map.Entry; 71 import java.util.Set; 72 import java.util.regex.Pattern; 73 74 /** 75 * A concrete {@link IConfiguration} implementation that stores the loaded config objects in a map. 76 */ 77 public class Configuration implements IConfiguration { 78 79 // type names for built in configuration objects 80 public static final String BUILD_PROVIDER_TYPE_NAME = "build_provider"; 81 public static final String TARGET_PREPARER_TYPE_NAME = "target_preparer"; 82 // Variation of Multi_target_preparer that runs BEFORE each device target_preparer. 83 public static final String MULTI_PRE_TARGET_PREPARER_TYPE_NAME = "multi_pre_target_preparer"; 84 public static final String MULTI_PREPARER_TYPE_NAME = "multi_target_preparer"; 85 public static final String TEST_TYPE_NAME = "test"; 86 public static final String DEVICE_RECOVERY_TYPE_NAME = "device_recovery"; 87 public static final String LOGGER_TYPE_NAME = "logger"; 88 public static final String LOG_SAVER_TYPE_NAME = "log_saver"; 89 public static final String RESULT_REPORTER_TYPE_NAME = "result_reporter"; 90 public static final String CMD_OPTIONS_TYPE_NAME = "cmd_options"; 91 public static final String DEVICE_REQUIREMENTS_TYPE_NAME = "device_requirements"; 92 public static final String DEVICE_OPTIONS_TYPE_NAME = "device_options"; 93 public static final String SYSTEM_STATUS_CHECKER_TYPE_NAME = "system_checker"; 94 public static final String CONFIGURATION_DESCRIPTION_TYPE_NAME = "config_desc"; 95 public static final String DEVICE_NAME = "device"; 96 public static final String DEVICE_METRICS_COLLECTOR_TYPE_NAME = "metrics_collector"; 97 public static final String DEVICE_SIDE_SPEC_TYPE_NAME = "device_side_collector_spec"; 98 public static final String METRIC_POST_PROCESSOR_TYPE_NAME = "metric_post_processor"; 99 public static final String SANDBOX_TYPE_NAME = "sandbox"; 100 public static final String SANBOX_OPTIONS_TYPE_NAME = "sandbox_options"; 101 102 private static Map<String, ObjTypeInfo> sObjTypeMap = null; 103 private static Set<String> sMultiDeviceSupportedTag = null; 104 105 // regexp pattern used to parse map option values 106 private static final Pattern OPTION_KEY_VALUE_PATTERN = Pattern.compile("(?<!\\\\)="); 107 108 private static final String CONFIG_EXCEPTION_PATTERN = "Could not find option with name "; 109 110 /** Mapping of config object type name to config objects. */ 111 private Map<String, List<Object>> mConfigMap; 112 private final String mName; 113 private final String mDescription; 114 // original command line used to create this given configuration. 115 private String[] mCommandLine; 116 117 // Used to track config names that were used to set field values 118 private MultiMap<FieldDef, String> mFieldSources = new MultiMap<>(); 119 // used to track the files that where dynamically downloaded 120 private Set<File> mRemoteFiles = new HashSet<>(); 121 122 /** 123 * Container struct for built-in config object type 124 */ 125 private static class ObjTypeInfo { 126 final Class<?> mExpectedType; 127 /** 128 * true if a list (ie many objects in a single config) are supported for this type 129 */ 130 final boolean mIsListSupported; 131 ObjTypeInfo(Class<?> expectedType, boolean isList)132 ObjTypeInfo(Class<?> expectedType, boolean isList) { 133 mExpectedType = expectedType; 134 mIsListSupported = isList; 135 } 136 } 137 138 /** 139 * Determine if given config object type name is a built in object 140 * 141 * @param typeName the config object type name 142 * @return <code>true</code> if name is a built in object type 143 */ isBuiltInObjType(String typeName)144 static boolean isBuiltInObjType(String typeName) { 145 return getObjTypeMap().containsKey(typeName); 146 } 147 getObjTypeMap()148 private static synchronized Map<String, ObjTypeInfo> getObjTypeMap() { 149 if (sObjTypeMap == null) { 150 sObjTypeMap = new HashMap<String, ObjTypeInfo>(); 151 sObjTypeMap.put(BUILD_PROVIDER_TYPE_NAME, new ObjTypeInfo(IBuildProvider.class, false)); 152 sObjTypeMap.put(TARGET_PREPARER_TYPE_NAME, 153 new ObjTypeInfo(ITargetPreparer.class, true)); 154 sObjTypeMap.put( 155 MULTI_PRE_TARGET_PREPARER_TYPE_NAME, 156 new ObjTypeInfo(IMultiTargetPreparer.class, true)); 157 sObjTypeMap.put(MULTI_PREPARER_TYPE_NAME, 158 new ObjTypeInfo(IMultiTargetPreparer.class, true)); 159 sObjTypeMap.put(TEST_TYPE_NAME, new ObjTypeInfo(IRemoteTest.class, true)); 160 sObjTypeMap.put(DEVICE_RECOVERY_TYPE_NAME, 161 new ObjTypeInfo(IDeviceRecovery.class, false)); 162 sObjTypeMap.put(LOGGER_TYPE_NAME, new ObjTypeInfo(ILeveledLogOutput.class, false)); 163 sObjTypeMap.put(LOG_SAVER_TYPE_NAME, new ObjTypeInfo(ILogSaver.class, false)); 164 sObjTypeMap.put(RESULT_REPORTER_TYPE_NAME, 165 new ObjTypeInfo(ITestInvocationListener.class, true)); 166 sObjTypeMap.put(CMD_OPTIONS_TYPE_NAME, new ObjTypeInfo(ICommandOptions.class, 167 false)); 168 sObjTypeMap.put(DEVICE_REQUIREMENTS_TYPE_NAME, new ObjTypeInfo(IDeviceSelection.class, 169 false)); 170 sObjTypeMap.put(DEVICE_OPTIONS_TYPE_NAME, new ObjTypeInfo(TestDeviceOptions.class, 171 false)); 172 sObjTypeMap.put(DEVICE_NAME, new ObjTypeInfo(IDeviceConfiguration.class, true)); 173 sObjTypeMap.put(SYSTEM_STATUS_CHECKER_TYPE_NAME, 174 new ObjTypeInfo(ISystemStatusChecker.class, true)); 175 sObjTypeMap.put( 176 CONFIGURATION_DESCRIPTION_TYPE_NAME, 177 new ObjTypeInfo(ConfigurationDescriptor.class, false)); 178 sObjTypeMap.put( 179 DEVICE_METRICS_COLLECTOR_TYPE_NAME, 180 new ObjTypeInfo(IMetricCollector.class, true)); 181 sObjTypeMap.put( 182 DEVICE_SIDE_SPEC_TYPE_NAME, 183 new ObjTypeInfo(DeviceSideCollectorSpecification.class, false)); 184 sObjTypeMap.put( 185 METRIC_POST_PROCESSOR_TYPE_NAME, 186 new ObjTypeInfo(BasePostProcessor.class, true)); 187 sObjTypeMap.put(SANBOX_OPTIONS_TYPE_NAME, new ObjTypeInfo(SandboxOptions.class, false)); 188 } 189 return sObjTypeMap; 190 } 191 192 /** 193 * Determine if a given config object type is allowed to exists inside a device tag 194 * configuration. 195 * Authorized type are: build_provider, target_preparer, device_recovery, device_requirements, 196 * device_options 197 * 198 * @param typeName the config object type name 199 * @return True if name is allowed to exists inside the device tag 200 */ doesBuiltInObjSupportMultiDevice(String typeName)201 static boolean doesBuiltInObjSupportMultiDevice(String typeName) { 202 return getMultiDeviceSupportedTag().contains(typeName); 203 } 204 205 /** 206 * Return the {@link Set} of tags that are supported in a device tag for multi device 207 * configuration. 208 */ getMultiDeviceSupportedTag()209 private static synchronized Set<String> getMultiDeviceSupportedTag() { 210 if (sMultiDeviceSupportedTag == null) { 211 sMultiDeviceSupportedTag = new HashSet<String>(); 212 sMultiDeviceSupportedTag.add(BUILD_PROVIDER_TYPE_NAME); 213 sMultiDeviceSupportedTag.add(TARGET_PREPARER_TYPE_NAME); 214 sMultiDeviceSupportedTag.add(DEVICE_RECOVERY_TYPE_NAME); 215 sMultiDeviceSupportedTag.add(DEVICE_REQUIREMENTS_TYPE_NAME); 216 sMultiDeviceSupportedTag.add(DEVICE_OPTIONS_TYPE_NAME); 217 } 218 return sMultiDeviceSupportedTag; 219 } 220 221 /** 222 * Creates an {@link Configuration} with default config objects. 223 */ Configuration(String name, String description)224 public Configuration(String name, String description) { 225 mName = name; 226 mDescription = description; 227 mConfigMap = new LinkedHashMap<String, List<Object>>(); 228 setDeviceConfig(new DeviceConfigurationHolder(ConfigurationDef.DEFAULT_DEVICE_NAME)); 229 setCommandOptions(new CommandOptions()); 230 setTest(new StubTest()); 231 setLogOutput(new StdoutLogger()); 232 setLogSaver(new FileSystemLogSaver()); // FileSystemLogSaver saves to tmp by default. 233 setTestInvocationListener(new TextResultReporter()); 234 // Init an empty list of target_preparers 235 setConfigurationObjectListNoThrow(TARGET_PREPARER_TYPE_NAME, new ArrayList<>()); 236 setMultiPreTargetPreparers(new ArrayList<>()); 237 setMultiTargetPreparers(new ArrayList<>()); 238 setSystemStatusCheckers(new ArrayList<ISystemStatusChecker>()); 239 setConfigurationDescriptor(new ConfigurationDescriptor()); 240 setDeviceMetricCollectors(new ArrayList<>()); 241 setPostProcessors(new ArrayList<>()); 242 setConfigurationObjectNoThrow(SANBOX_OPTIONS_TYPE_NAME, new SandboxOptions()); 243 } 244 245 /** 246 * If we are in multi device mode, we cannot allow fetching the regular references because 247 * they are most likely wrong. 248 */ notAllowedInMultiMode(String function)249 private void notAllowedInMultiMode(String function) { 250 if (getConfigurationObjectList(DEVICE_NAME).size() > 1) { 251 throw new UnsupportedOperationException(String.format("Calling %s is not allowed " 252 + "in multi device mode", function)); 253 } 254 if (getConfigurationObjectList(DEVICE_NAME).isEmpty()) { 255 throw new UnsupportedOperationException( 256 "We should always have at least 1 Device config"); 257 } 258 } 259 260 /** {@inheritDoc} */ 261 @Override getName()262 public String getName() { 263 return mName; 264 } 265 266 /** 267 * @return a short user readable description this {@link Configuration} 268 */ getDescription()269 public String getDescription() { 270 return mDescription; 271 } 272 273 /** 274 * {@inheritDoc} 275 */ 276 @Override setCommandLine(String[] arrayArgs)277 public void setCommandLine(String[] arrayArgs) { 278 mCommandLine = arrayArgs; 279 } 280 281 /** 282 * {@inheritDoc} 283 */ 284 @Override getCommandLine()285 public String getCommandLine() { 286 // FIXME: obfuscated passwords from command line. 287 if (mCommandLine != null && mCommandLine.length != 0) { 288 return QuotationAwareTokenizer.combineTokens(mCommandLine); 289 } 290 // If no args were available return null. 291 return null; 292 } 293 294 /** 295 * {@inheritDoc} 296 */ 297 @SuppressWarnings("unchecked") 298 @Override getBuildProvider()299 public IBuildProvider getBuildProvider() { 300 notAllowedInMultiMode("getBuildProvider"); 301 return ((List<IDeviceConfiguration>)getConfigurationObjectList(DEVICE_NAME)) 302 .get(0).getBuildProvider(); 303 } 304 305 /** 306 * {@inheritDoc} 307 */ 308 @SuppressWarnings("unchecked") 309 @Override getTargetPreparers()310 public List<ITargetPreparer> getTargetPreparers() { 311 notAllowedInMultiMode("getTargetPreparers"); 312 return ((List<IDeviceConfiguration>)getConfigurationObjectList(DEVICE_NAME)) 313 .get(0).getTargetPreparers(); 314 } 315 316 /** 317 * {@inheritDoc} 318 */ 319 @SuppressWarnings("unchecked") 320 @Override getTests()321 public List<IRemoteTest> getTests() { 322 return (List<IRemoteTest>) getConfigurationObjectList(TEST_TYPE_NAME); 323 } 324 325 /** 326 * {@inheritDoc} 327 */ 328 @SuppressWarnings("unchecked") 329 @Override getDeviceRecovery()330 public IDeviceRecovery getDeviceRecovery() { 331 notAllowedInMultiMode("getDeviceRecovery"); 332 return ((List<IDeviceConfiguration>)getConfigurationObjectList(DEVICE_NAME)) 333 .get(0).getDeviceRecovery(); 334 } 335 336 /** 337 * {@inheritDoc} 338 */ 339 @Override getLogOutput()340 public ILeveledLogOutput getLogOutput() { 341 return (ILeveledLogOutput) getConfigurationObject(LOGGER_TYPE_NAME); 342 } 343 344 /** 345 * {@inheritDoc} 346 */ 347 @Override getLogSaver()348 public ILogSaver getLogSaver() { 349 return (ILogSaver) getConfigurationObject(LOG_SAVER_TYPE_NAME); 350 } 351 352 /** 353 * {@inheritDoc} 354 */ 355 @SuppressWarnings("unchecked") 356 @Override getMultiTargetPreparers()357 public List<IMultiTargetPreparer> getMultiTargetPreparers() { 358 return (List<IMultiTargetPreparer>) getConfigurationObjectList(MULTI_PREPARER_TYPE_NAME); 359 } 360 361 /** {@inheritDoc} */ 362 @SuppressWarnings("unchecked") 363 @Override getMultiPreTargetPreparers()364 public List<IMultiTargetPreparer> getMultiPreTargetPreparers() { 365 return (List<IMultiTargetPreparer>) 366 getConfigurationObjectList(MULTI_PRE_TARGET_PREPARER_TYPE_NAME); 367 } 368 369 /** 370 * {@inheritDoc} 371 */ 372 @SuppressWarnings("unchecked") 373 @Override getSystemStatusCheckers()374 public List<ISystemStatusChecker> getSystemStatusCheckers() { 375 return (List<ISystemStatusChecker>) 376 getConfigurationObjectList(SYSTEM_STATUS_CHECKER_TYPE_NAME); 377 } 378 379 /** 380 * {@inheritDoc} 381 */ 382 @SuppressWarnings("unchecked") 383 @Override getTestInvocationListeners()384 public List<ITestInvocationListener> getTestInvocationListeners() { 385 return (List<ITestInvocationListener>) getConfigurationObjectList( 386 RESULT_REPORTER_TYPE_NAME); 387 } 388 389 /** {@inheritDoc} */ 390 @SuppressWarnings("unchecked") 391 @Override getMetricCollectors()392 public List<IMetricCollector> getMetricCollectors() { 393 return (List<IMetricCollector>) 394 getConfigurationObjectList(DEVICE_METRICS_COLLECTOR_TYPE_NAME); 395 } 396 397 @SuppressWarnings("unchecked") 398 @Override getPostProcessors()399 public List<IPostProcessor> getPostProcessors() { 400 return (List<IPostProcessor>) getConfigurationObjectList(METRIC_POST_PROCESSOR_TYPE_NAME); 401 } 402 403 /** {@inheritDoc} */ 404 @Override getDeviceSideCollectorsSpec()405 public DeviceSideCollectorSpecification getDeviceSideCollectorsSpec() { 406 return (DeviceSideCollectorSpecification) 407 getConfigurationObject(DEVICE_SIDE_SPEC_TYPE_NAME); 408 } 409 410 /** {@inheritDoc} */ 411 @Override getCommandOptions()412 public ICommandOptions getCommandOptions() { 413 return (ICommandOptions) getConfigurationObject(CMD_OPTIONS_TYPE_NAME); 414 } 415 416 /** {@inheritDoc} */ 417 @Override getConfigurationDescription()418 public ConfigurationDescriptor getConfigurationDescription() { 419 return (ConfigurationDescriptor) 420 getConfigurationObject(CONFIGURATION_DESCRIPTION_TYPE_NAME); 421 } 422 423 /** {@inheritDoc} */ 424 @SuppressWarnings("unchecked") 425 @Override getDeviceRequirements()426 public IDeviceSelection getDeviceRequirements() { 427 notAllowedInMultiMode("getDeviceRequirements"); 428 return ((List<IDeviceConfiguration>)getConfigurationObjectList(DEVICE_NAME)) 429 .get(0).getDeviceRequirements(); 430 } 431 432 /** 433 * {@inheritDoc} 434 */ 435 @SuppressWarnings("unchecked") 436 @Override getDeviceOptions()437 public TestDeviceOptions getDeviceOptions() { 438 notAllowedInMultiMode("getDeviceOptions"); 439 return ((List<IDeviceConfiguration>)getConfigurationObjectList(DEVICE_NAME)) 440 .get(0).getDeviceOptions(); 441 } 442 443 /** 444 * {@inheritDoc} 445 */ 446 @Override getConfigurationObjectList(String typeName)447 public List<?> getConfigurationObjectList(String typeName) { 448 return mConfigMap.get(typeName); 449 } 450 451 /** 452 * {@inheritDoc} 453 */ 454 @SuppressWarnings("unchecked") 455 @Override getDeviceConfigByName(String nameDevice)456 public IDeviceConfiguration getDeviceConfigByName(String nameDevice) { 457 for (IDeviceConfiguration deviceHolder : 458 (List<IDeviceConfiguration>)getConfigurationObjectList(DEVICE_NAME)) { 459 if (deviceHolder.getDeviceName().equals(nameDevice)) { 460 return deviceHolder; 461 } 462 } 463 return null; 464 } 465 466 /** 467 * {@inheritDoc} 468 */ 469 @SuppressWarnings("unchecked") 470 @Override getDeviceConfig()471 public List<IDeviceConfiguration> getDeviceConfig() { 472 return (List<IDeviceConfiguration>)getConfigurationObjectList(DEVICE_NAME); 473 } 474 475 /** 476 * {@inheritDoc} 477 */ 478 @Override getConfigurationObject(String typeName)479 public Object getConfigurationObject(String typeName) { 480 List<?> configObjects = getConfigurationObjectList(typeName); 481 if (configObjects == null) { 482 return null; 483 } 484 ObjTypeInfo typeInfo = getObjTypeMap().get(typeName); 485 if (typeInfo != null && typeInfo.mIsListSupported) { 486 throw new IllegalStateException( 487 String.format( 488 "Wrong method call for type %s. Used getConfigurationObject() for a " 489 + "config object that is stored as a list", 490 typeName)); 491 } 492 if (configObjects.size() != 1) { 493 throw new IllegalStateException(String.format( 494 "Attempted to retrieve single object for %s, but %d are present", 495 typeName, configObjects.size())); 496 } 497 return configObjects.get(0); 498 } 499 500 /** {@inheritDoc} */ 501 @Override getAllConfigurationObjectsOfType(String configType)502 public Collection<Object> getAllConfigurationObjectsOfType(String configType) { 503 Collection<Object> objectsCopy = new ArrayList<Object>(); 504 if (doesBuiltInObjSupportMultiDevice(configType)) { 505 for (IDeviceConfiguration deviceConfig : getDeviceConfig()) { 506 objectsCopy.addAll(deviceConfig.getAllObjectOfType(configType)); 507 } 508 } else { 509 List<?> configObjects = getConfigurationObjectList(configType); 510 if (configObjects != null) { 511 objectsCopy.addAll(configObjects); 512 } 513 } 514 return objectsCopy; 515 } 516 517 /** 518 * Return a copy of all config objects 519 */ getAllConfigurationObjects()520 private Collection<Object> getAllConfigurationObjects() { 521 return getAllConfigurationObjects(null); 522 } 523 524 /** 525 * Return a copy of all config objects, minus the object configuration of the type specified. 526 * Returns all the config objects if param is null. 527 */ getAllConfigurationObjects(String excludedConfigName)528 private Collection<Object> getAllConfigurationObjects(String excludedConfigName) { 529 Collection<Object> objectsCopy = new ArrayList<Object>(); 530 for (Entry<String, List<Object>> entryList : mConfigMap.entrySet()) { 531 if (excludedConfigName != null) { 532 // Only add if not a descriptor config object type. 533 if (!excludedConfigName.equals(entryList.getKey())) { 534 objectsCopy.addAll(entryList.getValue()); 535 } 536 } else { 537 objectsCopy.addAll(entryList.getValue()); 538 } 539 } 540 return objectsCopy; 541 } 542 543 /** 544 * Creates an OptionSetter which is appropriate for setting options on all objects which 545 * will be returned by {@link #getAllConfigurationObjects}. 546 */ createOptionSetter()547 private OptionSetter createOptionSetter() throws ConfigurationException { 548 return new OptionSetter(getAllConfigurationObjects()); 549 } 550 551 /** 552 * Injects an option value into the set of configuration objects. 553 * 554 * Uses provided arguments as is and fails if arguments have invalid format or 555 * provided ambiguously, e.g. {@code optionKey} argument is provided for non-map option, 556 * or the value for an option of integer type cannot be parsed as an integer number. 557 * 558 * @param optionSetter setter to use for the injection 559 * @param optionName name of the option 560 * @param optionKey map key, if the option is of map type 561 * @param optionValue value of the option or map value, if the option is of map type 562 * @param source source of the option 563 * @throws ConfigurationException if option value cannot be injected 564 */ internalInjectOptionValue(OptionSetter optionSetter, String optionName, String optionKey, String optionValue, String source)565 private void internalInjectOptionValue(OptionSetter optionSetter, String optionName, 566 String optionKey, String optionValue, String source) throws ConfigurationException { 567 if (optionSetter == null) { 568 throw new IllegalArgumentException("optionSetter cannot be null"); 569 } 570 571 // Set all fields that match this option name / key 572 List<FieldDef> affectedFields = optionSetter.setOptionValue( 573 optionName, optionKey, optionValue); 574 575 boolean requiredForRerun = false; 576 // Update the source for each affected field 577 for (FieldDef field : affectedFields) { 578 requiredForRerun |= field.field.getAnnotation(Option.class).requiredForRerun(); 579 if (source != null) { 580 // Unless the field is a Collection or MultiMap entry, it can only have one source 581 if (!Collection.class.isAssignableFrom(field.field.getType()) && 582 !MultiMap.class.isAssignableFrom(field.field.getType())) { 583 mFieldSources.remove(field); 584 } 585 mFieldSources.put(field, source); 586 } else if (requiredForRerun) { 587 // Only need to check if the option is required for rerun once if it's set to true. 588 break; 589 } 590 } 591 592 if (requiredForRerun) { 593 OptionDef optionDef = new OptionDef(optionName, optionKey, optionValue, source, null); 594 getConfigurationDescription().addRerunOption(optionDef); 595 } 596 } 597 598 /** 599 * Injects an option value into the set of configuration objects. 600 * 601 * If the option to be set is of map type, an attempt to parse {@code optionValue} argument 602 * into key-value pair is made. In this case {@code optionValue} must have an equal sign 603 * separating a key and a value (e.g. my_key=my_value). 604 * In case a key or a value themselves contain an equal sign, this equal sign in them 605 * must be escaped using a backslash (e.g. a\=b=y\=z). 606 * 607 * @param optionSetter setter to use for the injection 608 * @param optionName name of the option 609 * @param optionValue value of the option 610 * @throws ConfigurationException if option value cannot be injected 611 */ internalInjectOptionValue(OptionSetter optionSetter, String optionName, String optionValue)612 private void internalInjectOptionValue(OptionSetter optionSetter, String optionName, 613 String optionValue) throws ConfigurationException { 614 // Cannot continue without optionSetter 615 if (optionSetter == null) { 616 throw new IllegalArgumentException("optionSetter cannot be null"); 617 } 618 619 // If the option is not a map, then the key is null... 620 if (!optionSetter.isMapOption(optionName)) { 621 internalInjectOptionValue(optionSetter, optionName, null, optionValue, null); 622 return; 623 } 624 625 // ..., otherwise try to parse the value to retrieve the key 626 String[] parts = OPTION_KEY_VALUE_PATTERN.split(optionValue); 627 if (parts.length != 2) { 628 throw new ConfigurationException(String.format( 629 "option '%s' has an invalid format for value %s:w", 630 optionName, optionValue)); 631 } 632 internalInjectOptionValue(optionSetter, optionName, 633 parts[0].replace("\\\\=", "="), parts[1].replace("\\\\=", "="), null); 634 } 635 636 /** 637 * {@inheritDoc} 638 */ 639 @Override injectOptionValue(String optionName, String optionValue)640 public void injectOptionValue(String optionName, String optionValue) 641 throws ConfigurationException { 642 internalInjectOptionValue(createOptionSetter(), optionName, optionValue); 643 } 644 645 /** 646 * {@inheritDoc} 647 */ 648 @Override injectOptionValue(String optionName, String optionKey, String optionValue)649 public void injectOptionValue(String optionName, String optionKey, String optionValue) 650 throws ConfigurationException { 651 internalInjectOptionValue(createOptionSetter(), optionName, optionKey, optionValue, null); 652 } 653 654 /** 655 * {@inheritDoc} 656 */ 657 @Override injectOptionValueWithSource(String optionName, String optionKey, String optionValue, String source)658 public void injectOptionValueWithSource(String optionName, String optionKey, String optionValue, 659 String source) throws ConfigurationException { 660 internalInjectOptionValue(createOptionSetter(), optionName, optionKey, optionValue, source); 661 } 662 663 /** 664 * {@inheritDoc} 665 */ 666 @Override injectOptionValues(List<OptionDef> optionDefs)667 public void injectOptionValues(List<OptionDef> optionDefs) throws ConfigurationException { 668 OptionSetter optionSetter = createOptionSetter(); 669 for (OptionDef optionDef : optionDefs) { 670 internalInjectOptionValue(optionSetter, optionDef.name, optionDef.key, optionDef.value, 671 optionDef.source); 672 } 673 } 674 675 /** 676 * Creates a shallow copy of this object. 677 */ 678 @Override clone()679 public Configuration clone() { 680 Configuration clone = new Configuration(getName(), getDescription()); 681 for (Map.Entry<String, List<Object>> entry : mConfigMap.entrySet()) { 682 if (DEVICE_NAME.equals(entry.getKey())) { 683 List<Object> newDeviceConfigList = new ArrayList<Object>(); 684 for (Object deviceConfig : entry.getValue()) { 685 IDeviceConfiguration config = ((IDeviceConfiguration)deviceConfig); 686 IDeviceConfiguration newDeviceConfig = config.clone(); 687 newDeviceConfigList.add(newDeviceConfig); 688 } 689 clone.setConfigurationObjectListNoThrow(entry.getKey(), newDeviceConfigList); 690 } else { 691 clone.setConfigurationObjectListNoThrow(entry.getKey(), entry.getValue()); 692 } 693 } 694 clone.setCommandLine(this.mCommandLine); 695 return clone; 696 } 697 addToDefaultDeviceConfig(Object obj)698 private void addToDefaultDeviceConfig(Object obj) { 699 try { 700 getDeviceConfigByName(ConfigurationDef.DEFAULT_DEVICE_NAME).addSpecificConfig(obj); 701 } catch (ConfigurationException e) { 702 // should never happen 703 throw new IllegalArgumentException(e); 704 } 705 } 706 707 /** 708 * {@inheritDoc} 709 */ 710 @Override setBuildProvider(IBuildProvider provider)711 public void setBuildProvider(IBuildProvider provider) { 712 notAllowedInMultiMode("setBuildProvider"); 713 addToDefaultDeviceConfig(provider); 714 } 715 716 /** 717 * {@inheritDoc} 718 */ 719 @Override setTestInvocationListeners(List<ITestInvocationListener> listeners)720 public void setTestInvocationListeners(List<ITestInvocationListener> listeners) { 721 setConfigurationObjectListNoThrow(RESULT_REPORTER_TYPE_NAME, listeners); 722 } 723 724 /** {@inheritDoc} */ 725 @Override setDeviceMetricCollectors(List<IMetricCollector> collectors)726 public void setDeviceMetricCollectors(List<IMetricCollector> collectors) { 727 setConfigurationObjectListNoThrow(DEVICE_METRICS_COLLECTOR_TYPE_NAME, collectors); 728 } 729 730 /** {@inheritDoc} */ 731 @Override setDeviceSideCollectorSpec(DeviceSideCollectorSpecification deviceCollectorSpec)732 public void setDeviceSideCollectorSpec(DeviceSideCollectorSpecification deviceCollectorSpec) { 733 setConfigurationObjectNoThrow(DEVICE_SIDE_SPEC_TYPE_NAME, deviceCollectorSpec); 734 } 735 736 /** {@inheritDoc} */ 737 @Override setPostProcessors(List<IPostProcessor> processors)738 public void setPostProcessors(List<IPostProcessor> processors) { 739 setConfigurationObjectListNoThrow(METRIC_POST_PROCESSOR_TYPE_NAME, processors); 740 } 741 742 /** 743 * {@inheritDoc} 744 */ 745 @Override setTestInvocationListener(ITestInvocationListener listener)746 public void setTestInvocationListener(ITestInvocationListener listener) { 747 setConfigurationObjectNoThrow(RESULT_REPORTER_TYPE_NAME, listener); 748 } 749 750 /** 751 * {@inheritDoc} 752 */ 753 @Override setDeviceConfig(IDeviceConfiguration deviceConfig)754 public void setDeviceConfig(IDeviceConfiguration deviceConfig) { 755 setConfigurationObjectNoThrow(DEVICE_NAME, deviceConfig); 756 } 757 758 /** 759 * {@inheritDoc} 760 */ 761 @Override setDeviceConfigList(List<IDeviceConfiguration> deviceConfigs)762 public void setDeviceConfigList(List<IDeviceConfiguration> deviceConfigs) { 763 setConfigurationObjectListNoThrow(DEVICE_NAME, deviceConfigs); 764 } 765 766 /** 767 * {@inheritDoc} 768 */ 769 @Override setTest(IRemoteTest test)770 public void setTest(IRemoteTest test) { 771 setConfigurationObjectNoThrow(TEST_TYPE_NAME, test); 772 } 773 774 /** 775 * {@inheritDoc} 776 */ 777 @Override setTests(List<IRemoteTest> tests)778 public void setTests(List<IRemoteTest> tests) { 779 setConfigurationObjectListNoThrow(TEST_TYPE_NAME, tests); 780 } 781 782 /** 783 * {@inheritDoc} 784 */ 785 @Override setMultiTargetPreparers(List<IMultiTargetPreparer> multiTargPreps)786 public void setMultiTargetPreparers(List<IMultiTargetPreparer> multiTargPreps) { 787 setConfigurationObjectListNoThrow(MULTI_PREPARER_TYPE_NAME, multiTargPreps); 788 } 789 790 /** 791 * {@inheritDoc} 792 */ 793 @Override setMultiTargetPreparer(IMultiTargetPreparer multiTargPrep)794 public void setMultiTargetPreparer(IMultiTargetPreparer multiTargPrep) { 795 setConfigurationObjectNoThrow(MULTI_PREPARER_TYPE_NAME, multiTargPrep); 796 } 797 798 /** {@inheritDoc} */ 799 @Override setMultiPreTargetPreparers(List<IMultiTargetPreparer> multiPreTargPreps)800 public void setMultiPreTargetPreparers(List<IMultiTargetPreparer> multiPreTargPreps) { 801 setConfigurationObjectListNoThrow(MULTI_PRE_TARGET_PREPARER_TYPE_NAME, multiPreTargPreps); 802 } 803 804 /** {@inheritDoc} */ 805 @Override setMultiPreTargetPreparer(IMultiTargetPreparer multiPreTargPrep)806 public void setMultiPreTargetPreparer(IMultiTargetPreparer multiPreTargPrep) { 807 setConfigurationObjectNoThrow(MULTI_PRE_TARGET_PREPARER_TYPE_NAME, multiPreTargPrep); 808 } 809 810 /** 811 * {@inheritDoc} 812 */ 813 @Override setSystemStatusCheckers(List<ISystemStatusChecker> systemCheckers)814 public void setSystemStatusCheckers(List<ISystemStatusChecker> systemCheckers) { 815 setConfigurationObjectListNoThrow(SYSTEM_STATUS_CHECKER_TYPE_NAME, systemCheckers); 816 } 817 818 /** 819 * {@inheritDoc} 820 */ 821 @Override setSystemStatusChecker(ISystemStatusChecker systemChecker)822 public void setSystemStatusChecker(ISystemStatusChecker systemChecker) { 823 setConfigurationObjectNoThrow(SYSTEM_STATUS_CHECKER_TYPE_NAME, systemChecker); 824 } 825 826 /** {@inheritDoc} */ 827 @Override setLogOutput(ILeveledLogOutput logger)828 public void setLogOutput(ILeveledLogOutput logger) { 829 setConfigurationObjectNoThrow(LOGGER_TYPE_NAME, logger); 830 } 831 832 /** {@inheritDoc} */ 833 @Override setLogSaver(ILogSaver logSaver)834 public void setLogSaver(ILogSaver logSaver) { 835 setConfigurationObjectNoThrow(LOG_SAVER_TYPE_NAME, logSaver); 836 } 837 838 /** Sets the {@link ConfigurationDescriptor} to be used in the configuration. */ setConfigurationDescriptor(ConfigurationDescriptor configDescriptor)839 private void setConfigurationDescriptor(ConfigurationDescriptor configDescriptor) { 840 setConfigurationObjectNoThrow(CONFIGURATION_DESCRIPTION_TYPE_NAME, configDescriptor); 841 } 842 843 /** {@inheritDoc} */ 844 @Override setDeviceRecovery(IDeviceRecovery recovery)845 public void setDeviceRecovery(IDeviceRecovery recovery) { 846 notAllowedInMultiMode("setDeviceRecovery"); 847 addToDefaultDeviceConfig(recovery); 848 } 849 850 /** 851 * {@inheritDoc} 852 */ 853 @Override setTargetPreparer(ITargetPreparer preparer)854 public void setTargetPreparer(ITargetPreparer preparer) { 855 notAllowedInMultiMode("setTargetPreparer"); 856 addToDefaultDeviceConfig(preparer); 857 } 858 859 /** {@inheritDoc} */ 860 @Override setTargetPreparers(List<ITargetPreparer> preparers)861 public void setTargetPreparers(List<ITargetPreparer> preparers) { 862 notAllowedInMultiMode("setTargetPreparers"); 863 getDeviceConfigByName(ConfigurationDef.DEFAULT_DEVICE_NAME).getTargetPreparers().clear(); 864 for (ITargetPreparer prep : preparers) { 865 addToDefaultDeviceConfig(prep); 866 } 867 } 868 869 /** 870 * {@inheritDoc} 871 */ 872 @Override setCommandOptions(ICommandOptions cmdOptions)873 public void setCommandOptions(ICommandOptions cmdOptions) { 874 setConfigurationObjectNoThrow(CMD_OPTIONS_TYPE_NAME, cmdOptions); 875 } 876 877 /** 878 * {@inheritDoc} 879 */ 880 @Override setDeviceRequirements(IDeviceSelection devRequirements)881 public void setDeviceRequirements(IDeviceSelection devRequirements) { 882 notAllowedInMultiMode("setDeviceRequirements"); 883 addToDefaultDeviceConfig(devRequirements); 884 } 885 886 /** 887 * {@inheritDoc} 888 */ 889 @Override setDeviceOptions(TestDeviceOptions devOptions)890 public void setDeviceOptions(TestDeviceOptions devOptions) { 891 notAllowedInMultiMode("setDeviceOptions"); 892 addToDefaultDeviceConfig(devOptions); 893 } 894 895 /** 896 * {@inheritDoc} 897 */ 898 @Override setConfigurationObject(String typeName, Object configObject)899 public synchronized void setConfigurationObject(String typeName, Object configObject) 900 throws ConfigurationException { 901 if (configObject == null) { 902 throw new IllegalArgumentException("configObject cannot be null"); 903 } 904 mConfigMap.remove(typeName); 905 addObject(typeName, configObject); 906 } 907 908 /** 909 * {@inheritDoc} 910 */ 911 @Override setConfigurationObjectList(String typeName, List<?> configList)912 public synchronized void setConfigurationObjectList(String typeName, List<?> configList) 913 throws ConfigurationException { 914 if (configList == null) { 915 throw new IllegalArgumentException("configList cannot be null"); 916 } 917 mConfigMap.remove(typeName); 918 mConfigMap.put(typeName, new ArrayList<Object>(1)); 919 for (Object configObject : configList) { 920 addObject(typeName, configObject); 921 } 922 } 923 924 /** {@inheritDoc} */ 925 @Override isDeviceConfiguredFake(String deviceName)926 public boolean isDeviceConfiguredFake(String deviceName) { 927 IDeviceConfiguration deviceConfig = getDeviceConfigByName(deviceName); 928 if (deviceConfig == null) { 929 return false; 930 } 931 return deviceConfig.isFake(); 932 } 933 934 /** 935 * Adds a loaded object to this configuration. 936 * 937 * @param typeName the unique object type name of the configuration object 938 * @param configObject the configuration object 939 * @throws ConfigurationException if object was not the correct type 940 */ addObject(String typeName, Object configObject)941 private synchronized void addObject(String typeName, Object configObject) 942 throws ConfigurationException { 943 List<Object> objList = mConfigMap.get(typeName); 944 if (objList == null) { 945 objList = new ArrayList<Object>(1); 946 mConfigMap.put(typeName, objList); 947 } 948 ObjTypeInfo typeInfo = getObjTypeMap().get(typeName); 949 if (typeInfo != null && !typeInfo.mExpectedType.isInstance(configObject)) { 950 throw new ConfigurationException(String.format( 951 "The config object %s is not the correct type. Expected %s, received %s", 952 typeName, typeInfo.mExpectedType.getCanonicalName(), 953 configObject.getClass().getCanonicalName())); 954 } 955 if (typeInfo != null && !typeInfo.mIsListSupported && objList.size() > 0) { 956 throw new ConfigurationException(String.format( 957 "Only one config object allowed for %s, but multiple were specified.", 958 typeName)); 959 } 960 objList.add(configObject); 961 if (configObject instanceof IConfigurationReceiver) { 962 ((IConfigurationReceiver) configObject).setConfiguration(this); 963 } 964 // Inject to object inside device holder too. 965 if (configObject instanceof IDeviceConfiguration) { 966 for (Object obj : ((IDeviceConfiguration) configObject).getAllObjects()) { 967 if (obj instanceof IConfigurationReceiver) { 968 ((IConfigurationReceiver) obj).setConfiguration(this); 969 } 970 } 971 } 972 } 973 974 /** 975 * A wrapper around {@link #setConfigurationObject(String, Object)} that 976 * will not throw {@link ConfigurationException}. 977 * <p/> 978 * Intended to be used in cases where its guaranteed that 979 * <var>configObject</var> is the correct type. 980 * 981 * @param typeName 982 * @param configObject 983 */ setConfigurationObjectNoThrow(String typeName, Object configObject)984 private void setConfigurationObjectNoThrow(String typeName, Object configObject) { 985 try { 986 setConfigurationObject(typeName, configObject); 987 } catch (ConfigurationException e) { 988 // should never happen 989 throw new IllegalArgumentException(e); 990 } 991 } 992 993 /** 994 * A wrapper around {@link #setConfigurationObjectList(String, List)} that 995 * will not throw {@link ConfigurationException}. 996 * <p/> 997 * Intended to be used in cases where its guaranteed that 998 * <var>configObject</var> is the correct type 999 * 1000 * @param typeName 1001 * @param configList 1002 */ setConfigurationObjectListNoThrow(String typeName, List<?> configList)1003 private void setConfigurationObjectListNoThrow(String typeName, List<?> configList) { 1004 try { 1005 setConfigurationObjectList(typeName, configList); 1006 } catch (ConfigurationException e) { 1007 // should never happen 1008 throw new IllegalArgumentException(e); 1009 } 1010 } 1011 1012 /** 1013 * {@inheritDoc} 1014 */ 1015 @Override setOptionsFromCommandLineArgs(List<String> listArgs)1016 public List<String> setOptionsFromCommandLineArgs(List<String> listArgs) 1017 throws ConfigurationException { 1018 return setOptionsFromCommandLineArgs(listArgs, null); 1019 } 1020 1021 /** 1022 * {@inheritDoc} 1023 */ 1024 @Override setOptionsFromCommandLineArgs(List<String> listArgs, IKeyStoreClient keyStoreClient)1025 public List<String> setOptionsFromCommandLineArgs(List<String> listArgs, 1026 IKeyStoreClient keyStoreClient) 1027 throws ConfigurationException { 1028 // We get all the objects except the one describing the Configuration itself which does not 1029 // allow passing its option via command line. 1030 ArgsOptionParser parser = 1031 new ArgsOptionParser( 1032 getAllConfigurationObjects(CONFIGURATION_DESCRIPTION_TYPE_NAME)); 1033 if (keyStoreClient != null) { 1034 parser.setKeyStore(keyStoreClient); 1035 } 1036 try { 1037 return parser.parse(listArgs); 1038 } catch (ConfigurationException e) { 1039 if (!e.getMessage().contains(CONFIG_EXCEPTION_PATTERN)) { 1040 throw e; 1041 } 1042 String optionName = e.getMessage().split(CONFIG_EXCEPTION_PATTERN)[1]; 1043 try { 1044 // In case the option exists in the config descriptor, we change the error message 1045 // to be more specific about why the option is rejected. 1046 OptionSetter setter = new OptionSetter(getConfigurationDescription()); 1047 setter.getTypeForOption(optionName); 1048 } catch (ConfigurationException stillThrowing) { 1049 // Throw the original exception since it cannot be found at all. 1050 throw e; 1051 } 1052 throw new OptionNotAllowedException( 1053 String.format( 1054 "Option %s cannot be specified via " 1055 + "command line. Only in the configuration xml.", 1056 optionName)); 1057 } 1058 } 1059 1060 /** 1061 * Outputs a command line usage help text for this configuration to given 1062 * printStream. 1063 * 1064 * @param out the {@link PrintStream} to use. 1065 * @throws ConfigurationException 1066 */ 1067 @Override printCommandUsage(boolean importantOnly, PrintStream out)1068 public void printCommandUsage(boolean importantOnly, PrintStream out) 1069 throws ConfigurationException { 1070 out.println(String.format("'%s' configuration: %s", getName(), getDescription())); 1071 out.println(); 1072 if (importantOnly) { 1073 out.println("Printing help for only the important options. " + 1074 "To see help for all options, use the --help-all flag"); 1075 out.println(); 1076 } 1077 for (Map.Entry<String, List<Object>> configObjectsEntry : mConfigMap.entrySet()) { 1078 for (Object configObject : configObjectsEntry.getValue()) { 1079 if (configObject instanceof IDeviceConfiguration) { 1080 // We expand the Device Config Object. 1081 for (Object subconfigObject : ((IDeviceConfiguration)configObject) 1082 .getAllObjects()) { 1083 printCommandUsageForObject(importantOnly, out, configObjectsEntry.getKey(), 1084 subconfigObject); 1085 } 1086 } else { 1087 printCommandUsageForObject(importantOnly, out, configObjectsEntry.getKey(), 1088 configObject); 1089 } 1090 } 1091 } 1092 } 1093 printCommandUsageForObject(boolean importantOnly, PrintStream out, String key, Object obj)1094 private void printCommandUsageForObject(boolean importantOnly, PrintStream out, String key, 1095 Object obj) throws ConfigurationException { 1096 String optionHelp = printOptionsForObject(importantOnly, key, obj); 1097 // only print help for object if optionHelp is non zero length 1098 if (optionHelp.length() > 0) { 1099 String classAlias = ""; 1100 if (obj.getClass().isAnnotationPresent(OptionClass.class)) { 1101 final OptionClass classAnnotation = obj.getClass().getAnnotation( 1102 OptionClass.class); 1103 classAlias = String.format("'%s' ", classAnnotation.alias()); 1104 } 1105 out.printf(" %s%s options:", classAlias, key); 1106 out.println(); 1107 out.print(optionHelp); 1108 out.println(); 1109 } 1110 } 1111 1112 /** 1113 * Get the JSON representation of a single {@link Option} field. 1114 */ 1115 @SuppressWarnings({ 1116 "unchecked", "rawtypes" 1117 }) getOptionJson(Object optionObject, Field field)1118 private JSONObject getOptionJson(Object optionObject, Field field) throws JSONException { 1119 // Build a JSON representation of the option 1120 JSONObject jsonOption = new JSONObject(); 1121 1122 // Store values from the @Option annotation 1123 Option option = field.getAnnotation(Option.class); 1124 jsonOption.put("name", option.name()); 1125 if (option.shortName() != Option.NO_SHORT_NAME) { 1126 jsonOption.put("shortName", option.shortName()); 1127 } 1128 jsonOption.put("description", option.description()); 1129 jsonOption.put("importance", option.importance()); 1130 jsonOption.put("mandatory", option.mandatory()); 1131 jsonOption.put("isTimeVal", option.isTimeVal()); 1132 jsonOption.put("updateRule", option.updateRule().name()); 1133 1134 // Store the field's class 1135 Type fieldType = field.getGenericType(); 1136 if (fieldType instanceof ParameterizedType) { 1137 // Resolve paramaterized type arguments 1138 Type[] paramTypes = ((ParameterizedType) fieldType).getActualTypeArguments(); 1139 String[] paramStrings = new String[paramTypes.length]; 1140 for (int i = 0; i < paramTypes.length; i++) { 1141 paramStrings[i] = ((Class<?>) paramTypes[i]).getName(); 1142 } 1143 1144 jsonOption.put("javaClass", String.format("%s<%s>", 1145 field.getType().getName(), Joiner.on(", ").join(paramStrings))); 1146 } else { 1147 jsonOption.put("javaClass", field.getType().getName()); 1148 } 1149 1150 // Store the field's value 1151 Object value = null; 1152 try { 1153 field.setAccessible(true); 1154 value = field.get(optionObject); 1155 1156 // Convert nulls to JSONObject.NULL 1157 if (value == null) { 1158 jsonOption.put("value", JSONObject.NULL); 1159 // Convert MuliMap values to a JSON representation 1160 } else if (value instanceof MultiMap) { 1161 MultiMap multimap = (MultiMap) value; 1162 JSONObject jsonValue = new JSONObject(); 1163 for (Object keyObj : multimap.keySet()) { 1164 jsonValue.put(keyObj.toString(), multimap.get(keyObj)); 1165 } 1166 jsonOption.put("value", jsonValue); 1167 // Convert Map values to JSON 1168 } else if (value instanceof Map) { 1169 jsonOption.put("value", new JSONObject((Map) value)); 1170 // For everything else, just use the default representation 1171 } else { 1172 jsonOption.put("value", value); 1173 } 1174 } catch (IllegalAccessException e) { 1175 // Shouldn't happen 1176 throw new RuntimeException(e); 1177 } 1178 1179 // Store the field's source 1180 // Maps and MultiMaps track sources per key, so use a JSONObject to 1181 // represent their sources 1182 if (Map.class.isAssignableFrom(field.getType())) { 1183 JSONObject jsonSourcesMap = new JSONObject(); 1184 if (value != null) { 1185 // For each entry in the map, store the source as a JSONArray 1186 for (Object key : ((Map) value).keySet()) { 1187 List<String> source = mFieldSources.get(new FieldDef(optionObject, field, key)); 1188 jsonSourcesMap.put(key.toString(), source == null ? new JSONArray() : source); 1189 } 1190 } 1191 jsonOption.put("source", jsonSourcesMap); 1192 1193 } else if (MultiMap.class.isAssignableFrom(field.getType())) { 1194 JSONObject jsonSourcesMap = new JSONObject(); 1195 if (value != null) { 1196 // For each entry in the map, store the sources as a JSONArray 1197 for (Object key : ((MultiMap) value).keySet()) { 1198 List<String> source = mFieldSources.get(new FieldDef(optionObject, field, key)); 1199 jsonSourcesMap.put(key.toString(), source == null ? new JSONArray() : source); 1200 } 1201 } 1202 jsonOption.put("source", jsonSourcesMap); 1203 1204 // Collections and regular objects only have one set of sources for 1205 // the whole field, so use 1206 // a JSONArray 1207 } else { 1208 List<String> source = mFieldSources.get(new FieldDef(optionObject, field, null)); 1209 jsonOption.put("source", source == null ? new JSONArray() : source); 1210 } 1211 1212 return jsonOption; 1213 } 1214 1215 /** 1216 * {@inheritDoc} 1217 */ 1218 @Override getJsonCommandUsage()1219 public JSONArray getJsonCommandUsage() throws JSONException { 1220 JSONArray ret = new JSONArray(); 1221 for (Map.Entry<String, List<Object>> configObjectsEntry : mConfigMap.entrySet()) { 1222 for (Object optionObject : configObjectsEntry.getValue()) { 1223 1224 // Build a JSON representation of the current class 1225 JSONObject jsonClass = new JSONObject(); 1226 jsonClass.put("name", configObjectsEntry.getKey()); 1227 String alias = null; 1228 if (optionObject.getClass().isAnnotationPresent(OptionClass.class)) { 1229 OptionClass optionClass = optionObject.getClass() 1230 .getAnnotation(OptionClass.class); 1231 alias = optionClass.alias(); 1232 } 1233 jsonClass.put("alias", alias == null ? JSONObject.NULL : alias); 1234 jsonClass.put("class", optionObject.getClass().getName()); 1235 1236 // For each of the @Option annotated fields 1237 Collection<Field> optionFields = OptionSetter 1238 .getOptionFieldsForClass(optionObject.getClass()); 1239 JSONArray jsonOptions = new JSONArray(); 1240 for (Field field : optionFields) { 1241 // Add the JSON field representation to the JSON class 1242 // representation 1243 jsonOptions.put(getOptionJson(optionObject, field)); 1244 } 1245 jsonClass.put("options", jsonOptions); 1246 1247 // Add the JSON class representation to the list 1248 ret.put(jsonClass); 1249 } 1250 } 1251 1252 return ret; 1253 } 1254 1255 /** 1256 * Prints out the available config options for given configuration object. 1257 * 1258 * @param importantOnly print only the important options 1259 * @param objectTypeName the config object type name. Used to generate more 1260 * descriptive error messages 1261 * @param configObject the config object 1262 * @return a {@link String} of option help text 1263 * @throws ConfigurationException 1264 */ printOptionsForObject(boolean importantOnly, String objectTypeName, Object configObject)1265 private String printOptionsForObject(boolean importantOnly, String objectTypeName, 1266 Object configObject) throws ConfigurationException { 1267 return ArgsOptionParser.getOptionHelp(importantOnly, configObject); 1268 } 1269 1270 /** 1271 * {@inheritDoc} 1272 */ 1273 @Override validateOptions()1274 public void validateOptions() throws ConfigurationException { 1275 validateOptions(true); 1276 } 1277 1278 /** {@inheritDoc} */ 1279 @Override validateOptions(boolean download)1280 public void validateOptions(boolean download) throws ConfigurationException { 1281 ArgsOptionParser argsParser = new ArgsOptionParser(getAllConfigurationObjects()); 1282 argsParser.validateMandatoryOptions(); 1283 ICommandOptions options = getCommandOptions(); 1284 if (options.getShardCount() != null && options.getShardCount() < 1) { 1285 throw new ConfigurationException("a shard count must be a positive number"); 1286 } 1287 if (options.getShardIndex() != null 1288 && (options.getShardCount() == null || options.getShardIndex() < 0 1289 || options.getShardIndex() >= options.getShardCount())) { 1290 throw new ConfigurationException("a shard index must be in range [0, shard count)"); 1291 } 1292 // Parent invocation for local sharding should not resolved the dynamic @option yet. 1293 if (options.getShardCount() != null && options.getShardIndex() == null) { 1294 download = false; 1295 CLog.w("Skipping download due to local sharding detected."); 1296 } 1297 if (download) { 1298 CLog.d("Resolve and download remote files from @Option"); 1299 // Setup and validate the GCS File paths 1300 mRemoteFiles.addAll(argsParser.validateRemoteFilePath()); 1301 } 1302 } 1303 1304 /** {@inheritDoc} */ 1305 @Override cleanDynamicOptionFiles()1306 public void cleanDynamicOptionFiles() { 1307 for (File file : mRemoteFiles) { 1308 FileUtil.recursiveDelete(file); 1309 } 1310 } 1311 1312 /** 1313 * {@inheritDoc} 1314 */ 1315 @Override dumpXml(PrintWriter output)1316 public void dumpXml(PrintWriter output) throws IOException { 1317 dumpXml(output, new ArrayList<String>()); 1318 } 1319 1320 /** {@inheritDoc} */ 1321 @Override dumpXml(PrintWriter output, List<String> excludeFilters)1322 public void dumpXml(PrintWriter output, List<String> excludeFilters) throws IOException { 1323 dumpXml(output, excludeFilters, true); 1324 } 1325 1326 /** {@inheritDoc} */ 1327 @Override dumpXml( PrintWriter output, List<String> excludeFilters, boolean printDeprecatedOptions)1328 public void dumpXml( 1329 PrintWriter output, List<String> excludeFilters, boolean printDeprecatedOptions) 1330 throws IOException { 1331 KXmlSerializer serializer = new KXmlSerializer(); 1332 serializer.setOutput(output); 1333 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 1334 serializer.startDocument("UTF-8", null); 1335 serializer.startTag(null, ConfigurationUtil.CONFIGURATION_NAME); 1336 1337 for (IMultiTargetPreparer multiPreTargerPrep : getMultiPreTargetPreparers()) { 1338 ConfigurationUtil.dumpClassToXml( 1339 serializer, 1340 MULTI_PRE_TARGET_PREPARER_TYPE_NAME, 1341 multiPreTargerPrep, 1342 excludeFilters, 1343 printDeprecatedOptions); 1344 } 1345 1346 for (IMultiTargetPreparer multipreparer : getMultiTargetPreparers()) { 1347 ConfigurationUtil.dumpClassToXml( 1348 serializer, 1349 MULTI_PREPARER_TYPE_NAME, 1350 multipreparer, 1351 excludeFilters, 1352 printDeprecatedOptions); 1353 } 1354 1355 if (getDeviceConfig().size() > 1) { 1356 // Handle multi device. 1357 for (IDeviceConfiguration deviceConfig : getDeviceConfig()) { 1358 serializer.startTag(null, Configuration.DEVICE_NAME); 1359 serializer.attribute(null, "name", deviceConfig.getDeviceName()); 1360 if (deviceConfig.isFake()) { 1361 serializer.attribute(null, "isFake", "true"); 1362 } 1363 ConfigurationUtil.dumpClassToXml( 1364 serializer, 1365 BUILD_PROVIDER_TYPE_NAME, 1366 deviceConfig.getBuildProvider(), 1367 excludeFilters, 1368 printDeprecatedOptions); 1369 for (ITargetPreparer preparer : deviceConfig.getTargetPreparers()) { 1370 ConfigurationUtil.dumpClassToXml( 1371 serializer, 1372 TARGET_PREPARER_TYPE_NAME, 1373 preparer, 1374 excludeFilters, 1375 printDeprecatedOptions); 1376 } 1377 ConfigurationUtil.dumpClassToXml( 1378 serializer, 1379 DEVICE_RECOVERY_TYPE_NAME, 1380 deviceConfig.getDeviceRecovery(), 1381 excludeFilters, 1382 printDeprecatedOptions); 1383 ConfigurationUtil.dumpClassToXml( 1384 serializer, 1385 DEVICE_REQUIREMENTS_TYPE_NAME, 1386 deviceConfig.getDeviceRequirements(), 1387 excludeFilters, 1388 printDeprecatedOptions); 1389 ConfigurationUtil.dumpClassToXml( 1390 serializer, 1391 DEVICE_OPTIONS_TYPE_NAME, 1392 deviceConfig.getDeviceOptions(), 1393 excludeFilters, 1394 printDeprecatedOptions); 1395 serializer.endTag(null, Configuration.DEVICE_NAME); 1396 } 1397 } else { 1398 // Put single device tags 1399 ConfigurationUtil.dumpClassToXml( 1400 serializer, 1401 BUILD_PROVIDER_TYPE_NAME, 1402 getBuildProvider(), 1403 excludeFilters, 1404 printDeprecatedOptions); 1405 for (ITargetPreparer preparer : getTargetPreparers()) { 1406 ConfigurationUtil.dumpClassToXml( 1407 serializer, 1408 TARGET_PREPARER_TYPE_NAME, 1409 preparer, 1410 excludeFilters, 1411 printDeprecatedOptions); 1412 } 1413 ConfigurationUtil.dumpClassToXml( 1414 serializer, 1415 DEVICE_RECOVERY_TYPE_NAME, 1416 getDeviceRecovery(), 1417 excludeFilters, 1418 printDeprecatedOptions); 1419 ConfigurationUtil.dumpClassToXml( 1420 serializer, 1421 DEVICE_REQUIREMENTS_TYPE_NAME, 1422 getDeviceRequirements(), 1423 excludeFilters, 1424 printDeprecatedOptions); 1425 ConfigurationUtil.dumpClassToXml( 1426 serializer, 1427 DEVICE_OPTIONS_TYPE_NAME, 1428 getDeviceOptions(), 1429 excludeFilters, 1430 printDeprecatedOptions); 1431 } 1432 for (IRemoteTest test : getTests()) { 1433 ConfigurationUtil.dumpClassToXml( 1434 serializer, TEST_TYPE_NAME, test, excludeFilters, printDeprecatedOptions); 1435 } 1436 ConfigurationUtil.dumpClassToXml( 1437 serializer, 1438 CONFIGURATION_DESCRIPTION_TYPE_NAME, 1439 getConfigurationDescription(), 1440 excludeFilters, 1441 printDeprecatedOptions); 1442 ConfigurationUtil.dumpClassToXml( 1443 serializer, 1444 LOGGER_TYPE_NAME, 1445 getLogOutput(), 1446 excludeFilters, 1447 printDeprecatedOptions); 1448 ConfigurationUtil.dumpClassToXml( 1449 serializer, 1450 LOG_SAVER_TYPE_NAME, 1451 getLogSaver(), 1452 excludeFilters, 1453 printDeprecatedOptions); 1454 for (ITestInvocationListener listener : getTestInvocationListeners()) { 1455 ConfigurationUtil.dumpClassToXml( 1456 serializer, 1457 RESULT_REPORTER_TYPE_NAME, 1458 listener, 1459 excludeFilters, 1460 printDeprecatedOptions); 1461 } 1462 ConfigurationUtil.dumpClassToXml( 1463 serializer, 1464 CMD_OPTIONS_TYPE_NAME, 1465 getCommandOptions(), 1466 excludeFilters, 1467 printDeprecatedOptions); 1468 1469 for (IMetricCollector collector : getMetricCollectors()) { 1470 ConfigurationUtil.dumpClassToXml( 1471 serializer, 1472 DEVICE_METRICS_COLLECTOR_TYPE_NAME, 1473 collector, 1474 excludeFilters, 1475 printDeprecatedOptions); 1476 } 1477 1478 for (ISystemStatusChecker checker : getSystemStatusCheckers()) { 1479 ConfigurationUtil.dumpClassToXml( 1480 serializer, 1481 SYSTEM_STATUS_CHECKER_TYPE_NAME, 1482 checker, 1483 excludeFilters, 1484 printDeprecatedOptions); 1485 } 1486 1487 ConfigurationUtil.dumpClassToXml( 1488 serializer, 1489 SANBOX_OPTIONS_TYPE_NAME, 1490 getConfigurationObject(SANBOX_OPTIONS_TYPE_NAME), 1491 excludeFilters, 1492 printDeprecatedOptions); 1493 1494 serializer.endTag(null, ConfigurationUtil.CONFIGURATION_NAME); 1495 serializer.endDocument(); 1496 } 1497 } 1498