/src/org/zeroxlab/wookieerunner/WookieeRunnerStarter.java
Java | 211 lines | 156 code | 23 blank | 32 comment | 14 complexity | f30a2927da5bf1f5913a7f2610f732e4 MD5 | raw file
Possible License(s): Apache-2.0, GPL-2.0
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 */ 19package org.zeroxlab.wookieerunner; 20 21import com.google.common.base.Predicate; 22import com.google.common.base.Predicates; 23import com.google.common.collect.ImmutableMap; 24 25import com.android.chimpchat.ChimpChat; 26 27import com.android.monkeyrunner.MonkeyFormatter; 28import com.android.monkeyrunner.MonkeyRunnerOptions; 29import com.android.monkeyrunner.doc.MonkeyRunnerExported; 30 31import org.zeroxlab.wookieerunner.ScriptRunner; 32 33import org.python.util.PythonInterpreter; 34 35import java.io.File; 36import java.io.IOException; 37import java.net.MalformedURLException; 38import java.net.URL; 39import java.net.URLClassLoader; 40import java.util.Enumeration; 41import java.util.Map; 42import java.util.TreeMap; 43import java.util.jar.Attributes; 44import java.util.jar.JarFile; 45import java.util.jar.Manifest; 46import java.util.logging.Formatter; 47import java.util.logging.Handler; 48import java.util.logging.Level; 49import java.util.logging.LogManager; 50import java.util.logging.Logger; 51 52/** 53 * MonkeyRunner is a host side application to control a monkey instance on a 54 * device. MonkeyRunner provides some useful helper functions to control the 55 * device as well as various other methods to help script tests. This class bootstraps 56 * MonkeyRunner. 57 */ 58public class WookieeRunnerStarter { 59 private static final Logger LOG = Logger.getLogger(WookieeRunnerStarter.class.getName()); 60 private static final String MONKEY_RUNNER_MAIN_MANIFEST_NAME = "MonkeyRunnerStartupRunner"; 61 62 private final ChimpChat chimp; 63 private final MonkeyRunnerOptions options; 64 private final ScriptRunner scriptrunner; 65 66 public WookieeRunnerStarter(MonkeyRunnerOptions options) { 67 Map<String, String> chimp_options = new TreeMap<String, String>(); 68 chimp_options.put("backend", options.getBackendName()); 69 this.options = options; 70 this.chimp = ChimpChat.getInstance(chimp_options); 71 WookieeRunner.setChimpChat(chimp); 72 73 String wookieeRunnerPath = System.getProperty("com.android.wookieerunner.bindir") + 74 File.separator + "wookieerunner"; 75 Map<String, Predicate<PythonInterpreter>> plugins = handlePlugins(); 76 scriptrunner = ScriptRunner.newInstance(null, null, wookieeRunnerPath); 77 } 78 79 public void runString(String source) { 80 scriptrunner.runStringLocal(source); 81 } 82 83 private int run() { 84 // This system property gets set by the included starter script 85 String wookieeRunnerPath = System.getProperty("com.android.wookieerunner.bindir") + 86 File.separator + "wookieerunner"; 87 88 Map<String, Predicate<PythonInterpreter>> plugins = handlePlugins(); 89 if (options.getScriptFile() == null) { 90 ScriptRunner.console(wookieeRunnerPath); 91 chimp.shutdown(); 92 return 0; 93 } else { 94 int error = ScriptRunner.run(wookieeRunnerPath, options.getScriptFile().getAbsolutePath(), 95 options.getArguments(), plugins); 96 chimp.shutdown(); 97 return error; 98 } 99 } 100 101 private Predicate<PythonInterpreter> handlePlugin(File f) { 102 JarFile jarFile; 103 try { 104 jarFile = new JarFile(f); 105 } catch (IOException e) { 106 LOG.log(Level.SEVERE, "Unable to open plugin file. Is it a jar file? " + 107 f.getAbsolutePath(), e); 108 return Predicates.alwaysFalse(); 109 } 110 Manifest manifest; 111 try { 112 manifest = jarFile.getManifest(); 113 } catch (IOException e) { 114 LOG.log(Level.SEVERE, "Unable to get manifest file from jar: " + 115 f.getAbsolutePath(), e); 116 return Predicates.alwaysFalse(); 117 } 118 Attributes mainAttributes = manifest.getMainAttributes(); 119 String pluginClass = mainAttributes.getValue(MONKEY_RUNNER_MAIN_MANIFEST_NAME); 120 if (pluginClass == null) { 121 // No main in this plugin, so it always succeeds. 122 return Predicates.alwaysTrue(); 123 } 124 URL url; 125 try { 126 url = f.toURI().toURL(); 127 } catch (MalformedURLException e) { 128 LOG.log(Level.SEVERE, "Unable to convert file to url " + f.getAbsolutePath(), 129 e); 130 return Predicates.alwaysFalse(); 131 } 132 URLClassLoader classLoader = new URLClassLoader(new URL[] { url }, 133 ClassLoader.getSystemClassLoader()); 134 Class<?> clz; 135 try { 136 clz = Class.forName(pluginClass, true, classLoader); 137 } catch (ClassNotFoundException e) { 138 LOG.log(Level.SEVERE, "Unable to load the specified plugin: " + pluginClass, e); 139 return Predicates.alwaysFalse(); 140 } 141 Object loadedObject; 142 try { 143 loadedObject = clz.newInstance(); 144 } catch (InstantiationException e) { 145 LOG.log(Level.SEVERE, "Unable to load the specified plugin: " + pluginClass, e); 146 return Predicates.alwaysFalse(); 147 } catch (IllegalAccessException e) { 148 LOG.log(Level.SEVERE, "Unable to load the specified plugin " + 149 "(did you make it public?): " + pluginClass, e); 150 return Predicates.alwaysFalse(); 151 } 152 // Cast it to the right type 153 if (loadedObject instanceof Runnable) { 154 final Runnable run = (Runnable) loadedObject; 155 return new Predicate<PythonInterpreter>() { 156 public boolean apply(PythonInterpreter i) { 157 run.run(); 158 return true; 159 } 160 }; 161 } else if (loadedObject instanceof Predicate<?>) { 162 return (Predicate<PythonInterpreter>) loadedObject; 163 } else { 164 LOG.severe("Unable to coerce object into correct type: " + pluginClass); 165 return Predicates.alwaysFalse(); 166 } 167 } 168 169 private Map<String, Predicate<PythonInterpreter>> handlePlugins() { 170 ImmutableMap.Builder<String, Predicate<PythonInterpreter>> builder = ImmutableMap.builder(); 171 for (File f : options.getPlugins()) { 172 builder.put(f.getAbsolutePath(), handlePlugin(f)); 173 } 174 return builder.build(); 175 } 176 177 /* Similar to above, when this fails, it no longer throws a 178 * runtime exception, but merely will log the failure. 179 */ 180 181 182 private static final void replaceAllLogFormatters(Formatter form, Level level) { 183 LogManager mgr = LogManager.getLogManager(); 184 Enumeration<String> loggerNames = mgr.getLoggerNames(); 185 while (loggerNames.hasMoreElements()) { 186 String loggerName = loggerNames.nextElement(); 187 Logger logger = mgr.getLogger(loggerName); 188 for (Handler handler : logger.getHandlers()) { 189 handler.setFormatter(form); 190 handler.setLevel(level); 191 } 192 } 193 } 194 195 public static void main(String[] args) { 196 MonkeyRunnerOptions options = MonkeyRunnerOptions.processOptions(args); 197 198 if (options == null) { 199 return; 200 } 201 202 // logging property files are difficult 203 replaceAllLogFormatters(MonkeyFormatter.DEFAULT_INSTANCE, options.getLogLevel()); 204 205 WookieeRunnerStarter runner = new WookieeRunnerStarter(options); 206 int error = runner.run(); 207 208 // This will kill any background threads as well. 209 System.exit(error); 210 } 211}