1 /* 2 * Copyright (C) 2011 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.command; 18 19 import com.android.tradefed.config.ConfigurationException; 20 import com.android.tradefed.config.GlobalConfiguration; 21 import com.android.tradefed.device.NoDeviceException; 22 23 import com.google.common.annotations.VisibleForTesting; 24 25 /** 26 * An alternate TradeFederation entry point that will run command specified in command 27 * line arguments and then quit. 28 * <p/> 29 * Intended for use with a debugger and other non-interactive modes of operation. 30 * <p/> 31 * Expected arguments: [commands options] (config to run) 32 */ 33 public class CommandRunner { 34 private ICommandScheduler mScheduler; 35 private ExitCode mErrorCode = ExitCode.NO_ERROR; 36 37 private static final long CHECK_DEVICE_TIMEOUT = 60000; 38 CommandRunner()39 public CommandRunner() {} 40 getErrorCode()41 public ExitCode getErrorCode() { 42 return mErrorCode; 43 } 44 45 /** 46 * Initialize the required global configuration. 47 */ 48 @VisibleForTesting initGlobalConfig(String[] args)49 void initGlobalConfig(String[] args) throws ConfigurationException { 50 GlobalConfiguration.createGlobalConfiguration(args); 51 } 52 53 /** Get the {@link ICommandScheduler} instance from the global configuration. */ 54 @VisibleForTesting getCommandScheduler()55 ICommandScheduler getCommandScheduler() { 56 return GlobalConfiguration.getInstance().getCommandScheduler(); 57 } 58 59 /** Prints the exception stack to stderr. */ 60 @VisibleForTesting printStackTrace(Throwable e)61 void printStackTrace(Throwable e) { 62 e.printStackTrace(); 63 } 64 65 /** Returns the timeout after which to check for the command. */ 66 @VisibleForTesting getCheckDeviceTimeout()67 long getCheckDeviceTimeout() { 68 return CHECK_DEVICE_TIMEOUT; 69 } 70 71 /** 72 * The main method to run the command. 73 * 74 * @param args the config name to run and its options 75 */ run(String[] args)76 public void run(String[] args) { 77 try { 78 initGlobalConfig(args); 79 mScheduler = getCommandScheduler(); 80 mScheduler.start(); 81 mScheduler.addCommand(args); 82 } catch (ConfigurationException e) { 83 printStackTrace(e); 84 mErrorCode = ExitCode.CONFIG_EXCEPTION; 85 } finally { 86 mScheduler.shutdownOnEmpty(); 87 } 88 try { 89 mScheduler.join(getCheckDeviceTimeout()); 90 // FIXME: if possible make the no_device allocated check deterministic. 91 // After 1 min we check if the command was executed. 92 if (mScheduler.getReadyCommandCount() > 0) { 93 printStackTrace(new NoDeviceException("No device was allocated for the command.")); 94 mErrorCode = ExitCode.NO_DEVICE_ALLOCATED; 95 mScheduler.removeAllCommands(); 96 mScheduler.shutdown(); 97 return; 98 } 99 mScheduler.join(); 100 // If no error code has been raised yet, we checked the invocation error code. 101 if (ExitCode.NO_ERROR.equals(mErrorCode)) { 102 mErrorCode = mScheduler.getLastInvocationExitCode(); 103 } 104 } catch (InterruptedException e) { 105 e.printStackTrace(); 106 mErrorCode = ExitCode.THROWABLE_EXCEPTION; 107 } 108 if (!ExitCode.NO_ERROR.equals(mErrorCode) 109 && mScheduler.getLastInvocationThrowable() != null) { 110 // Print error to the stderr so that it can be recovered. 111 printStackTrace(mScheduler.getLastInvocationThrowable()); 112 } 113 } 114 main(final String[] mainArgs)115 public static void main(final String[] mainArgs) { 116 CommandRunner console = new CommandRunner(); 117 console.run(mainArgs); 118 System.exit(console.getErrorCode().getCodeValue()); 119 } 120 121 /** 122 * Error codes that are possible to exit with. 123 */ 124 public static enum ExitCode { 125 NO_ERROR(0), 126 CONFIG_EXCEPTION(1), 127 NO_BUILD(2), 128 DEVICE_UNRESPONSIVE(3), 129 DEVICE_UNAVAILABLE(4), 130 FATAL_HOST_ERROR(5), 131 THROWABLE_EXCEPTION(6), 132 NO_DEVICE_ALLOCATED(7); 133 134 private final int mCodeValue; 135 ExitCode(int codeValue)136 ExitCode(int codeValue) { 137 mCodeValue = codeValue; 138 } 139 getCodeValue()140 public int getCodeValue() { 141 return mCodeValue; 142 } 143 } 144 } 145