/src/org/zeroxlab/wookieerunner/WookieeRunnerStarter.java

https://code.google.com/ · Java · 211 lines · 156 code · 23 blank · 32 comment · 14 complexity · f30a2927da5bf1f5913a7f2610f732e4 MD5 · raw file

  1. /*
  2. * Copyright (C) 2011 0xlab - http://0xlab.org/
  3. * Copyright (C) 2010 The Android Open Source Project
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. * Owl integration for Aster by Wei-Ning Huang <azhuang@0xlab.org>
  18. */
  19. package org.zeroxlab.wookieerunner;
  20. import com.google.common.base.Predicate;
  21. import com.google.common.base.Predicates;
  22. import com.google.common.collect.ImmutableMap;
  23. import com.android.chimpchat.ChimpChat;
  24. import com.android.monkeyrunner.MonkeyFormatter;
  25. import com.android.monkeyrunner.MonkeyRunnerOptions;
  26. import com.android.monkeyrunner.doc.MonkeyRunnerExported;
  27. import org.zeroxlab.wookieerunner.ScriptRunner;
  28. import org.python.util.PythonInterpreter;
  29. import java.io.File;
  30. import java.io.IOException;
  31. import java.net.MalformedURLException;
  32. import java.net.URL;
  33. import java.net.URLClassLoader;
  34. import java.util.Enumeration;
  35. import java.util.Map;
  36. import java.util.TreeMap;
  37. import java.util.jar.Attributes;
  38. import java.util.jar.JarFile;
  39. import java.util.jar.Manifest;
  40. import java.util.logging.Formatter;
  41. import java.util.logging.Handler;
  42. import java.util.logging.Level;
  43. import java.util.logging.LogManager;
  44. import java.util.logging.Logger;
  45. /**
  46. * MonkeyRunner is a host side application to control a monkey instance on a
  47. * device. MonkeyRunner provides some useful helper functions to control the
  48. * device as well as various other methods to help script tests. This class bootstraps
  49. * MonkeyRunner.
  50. */
  51. public class WookieeRunnerStarter {
  52. private static final Logger LOG = Logger.getLogger(WookieeRunnerStarter.class.getName());
  53. private static final String MONKEY_RUNNER_MAIN_MANIFEST_NAME = "MonkeyRunnerStartupRunner";
  54. private final ChimpChat chimp;
  55. private final MonkeyRunnerOptions options;
  56. private final ScriptRunner scriptrunner;
  57. public WookieeRunnerStarter(MonkeyRunnerOptions options) {
  58. Map<String, String> chimp_options = new TreeMap<String, String>();
  59. chimp_options.put("backend", options.getBackendName());
  60. this.options = options;
  61. this.chimp = ChimpChat.getInstance(chimp_options);
  62. WookieeRunner.setChimpChat(chimp);
  63. String wookieeRunnerPath = System.getProperty("com.android.wookieerunner.bindir") +
  64. File.separator + "wookieerunner";
  65. Map<String, Predicate<PythonInterpreter>> plugins = handlePlugins();
  66. scriptrunner = ScriptRunner.newInstance(null, null, wookieeRunnerPath);
  67. }
  68. public void runString(String source) {
  69. scriptrunner.runStringLocal(source);
  70. }
  71. private int run() {
  72. // This system property gets set by the included starter script
  73. String wookieeRunnerPath = System.getProperty("com.android.wookieerunner.bindir") +
  74. File.separator + "wookieerunner";
  75. Map<String, Predicate<PythonInterpreter>> plugins = handlePlugins();
  76. if (options.getScriptFile() == null) {
  77. ScriptRunner.console(wookieeRunnerPath);
  78. chimp.shutdown();
  79. return 0;
  80. } else {
  81. int error = ScriptRunner.run(wookieeRunnerPath, options.getScriptFile().getAbsolutePath(),
  82. options.getArguments(), plugins);
  83. chimp.shutdown();
  84. return error;
  85. }
  86. }
  87. private Predicate<PythonInterpreter> handlePlugin(File f) {
  88. JarFile jarFile;
  89. try {
  90. jarFile = new JarFile(f);
  91. } catch (IOException e) {
  92. LOG.log(Level.SEVERE, "Unable to open plugin file. Is it a jar file? " +
  93. f.getAbsolutePath(), e);
  94. return Predicates.alwaysFalse();
  95. }
  96. Manifest manifest;
  97. try {
  98. manifest = jarFile.getManifest();
  99. } catch (IOException e) {
  100. LOG.log(Level.SEVERE, "Unable to get manifest file from jar: " +
  101. f.getAbsolutePath(), e);
  102. return Predicates.alwaysFalse();
  103. }
  104. Attributes mainAttributes = manifest.getMainAttributes();
  105. String pluginClass = mainAttributes.getValue(MONKEY_RUNNER_MAIN_MANIFEST_NAME);
  106. if (pluginClass == null) {
  107. // No main in this plugin, so it always succeeds.
  108. return Predicates.alwaysTrue();
  109. }
  110. URL url;
  111. try {
  112. url = f.toURI().toURL();
  113. } catch (MalformedURLException e) {
  114. LOG.log(Level.SEVERE, "Unable to convert file to url " + f.getAbsolutePath(),
  115. e);
  116. return Predicates.alwaysFalse();
  117. }
  118. URLClassLoader classLoader = new URLClassLoader(new URL[] { url },
  119. ClassLoader.getSystemClassLoader());
  120. Class<?> clz;
  121. try {
  122. clz = Class.forName(pluginClass, true, classLoader);
  123. } catch (ClassNotFoundException e) {
  124. LOG.log(Level.SEVERE, "Unable to load the specified plugin: " + pluginClass, e);
  125. return Predicates.alwaysFalse();
  126. }
  127. Object loadedObject;
  128. try {
  129. loadedObject = clz.newInstance();
  130. } catch (InstantiationException e) {
  131. LOG.log(Level.SEVERE, "Unable to load the specified plugin: " + pluginClass, e);
  132. return Predicates.alwaysFalse();
  133. } catch (IllegalAccessException e) {
  134. LOG.log(Level.SEVERE, "Unable to load the specified plugin " +
  135. "(did you make it public?): " + pluginClass, e);
  136. return Predicates.alwaysFalse();
  137. }
  138. // Cast it to the right type
  139. if (loadedObject instanceof Runnable) {
  140. final Runnable run = (Runnable) loadedObject;
  141. return new Predicate<PythonInterpreter>() {
  142. public boolean apply(PythonInterpreter i) {
  143. run.run();
  144. return true;
  145. }
  146. };
  147. } else if (loadedObject instanceof Predicate<?>) {
  148. return (Predicate<PythonInterpreter>) loadedObject;
  149. } else {
  150. LOG.severe("Unable to coerce object into correct type: " + pluginClass);
  151. return Predicates.alwaysFalse();
  152. }
  153. }
  154. private Map<String, Predicate<PythonInterpreter>> handlePlugins() {
  155. ImmutableMap.Builder<String, Predicate<PythonInterpreter>> builder = ImmutableMap.builder();
  156. for (File f : options.getPlugins()) {
  157. builder.put(f.getAbsolutePath(), handlePlugin(f));
  158. }
  159. return builder.build();
  160. }
  161. /* Similar to above, when this fails, it no longer throws a
  162. * runtime exception, but merely will log the failure.
  163. */
  164. private static final void replaceAllLogFormatters(Formatter form, Level level) {
  165. LogManager mgr = LogManager.getLogManager();
  166. Enumeration<String> loggerNames = mgr.getLoggerNames();
  167. while (loggerNames.hasMoreElements()) {
  168. String loggerName = loggerNames.nextElement();
  169. Logger logger = mgr.getLogger(loggerName);
  170. for (Handler handler : logger.getHandlers()) {
  171. handler.setFormatter(form);
  172. handler.setLevel(level);
  173. }
  174. }
  175. }
  176. public static void main(String[] args) {
  177. MonkeyRunnerOptions options = MonkeyRunnerOptions.processOptions(args);
  178. if (options == null) {
  179. return;
  180. }
  181. // logging property files are difficult
  182. replaceAllLogFormatters(MonkeyFormatter.DEFAULT_INSTANCE, options.getLogLevel());
  183. WookieeRunnerStarter runner = new WookieeRunnerStarter(options);
  184. int error = runner.run();
  185. // This will kill any background threads as well.
  186. System.exit(error);
  187. }
  188. }