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