• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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