1 package cz.cuni.amis.pogamut.ut2004.bot.sposh; 2 3 import java.io.IOException; 4 import java.io.Reader; 5 import java.util.List; 6 7 import javax.script.Invocable; 8 import javax.script.ScriptContext; 9 import javax.script.ScriptEngine; 10 import javax.script.ScriptEngineManager; 11 import javax.script.ScriptException; 12 13 import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004Bot; 14 import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004BotLogicController; 15 16 /** 17 * This is class for the agents that are using the ScriptEngine. ScriptEngine is 18 * a part of Java API that allows execution of scripting file using integrated engine. 19 * <p> 20 * This basically allows us to create a agent with logic implemented in some 21 * scripting language (like JavaScript or Python). Beware, Java has currently 22 * implemented only one engine and that is JavaScript, for others it is 23 * necessary to provide custom binding library for the language (like jython). 24 * <p> 25 * The script that will be executed shoudl contain all the normal functions 26 * that are expected, i.e.: 27 * <ul> 28 * <li>doLogic</li> 29 * <li>prePrepareAgent</li> 30 * <li>postPrepareAgent</li> 31 * <li>shutdownAgent</li> 32 * </ul> 33 * <p> 34 * You have to specify the file to load and engine to bind before 35 * starting the agent. 36 * 37 * Subclasses has to implement {@link StreamScriptedAgent.getScriptFile()} to 38 * specify the script. This is done mostly because of google Guice. 39 * 40 * Also, if subclass is utilizing some script that is not available as default, 41 * it should override {@link StreamScriptedAgent.engineIsGoingToBeBinded()} to set it up. 42 * 43 * TODO: this class is rather ugly, it deserves some clean up. 44 * <p> 45 * It should go approx. like this 46 * * Create a new instance 47 * * Create ScriptEngineManager 48 * * call engineIsGoingToBeBinded() 49 * * get engine from manager based on extension or something 50 * 51 * How to get data: 52 * 53 * get file 54 * get stream from file 55 * 56 * What can be done: 57 * <ol> 58 * <li>Override getScriptFile, in such case default get stream from file will be used. 59 * Engine will be created based on extension of file. 60 * </li> 61 * <li>Override getScriptStream, in that case getScriptFile won't be used, but 62 * user has to specify the type of engine.and 63 * 64 * </li> 65 * </or> 66 * 67 * @author Honza 68 */ 69 70 public abstract class StreamScriptLogic extends UT2004BotLogicController { 71 72 protected ScriptEngineManager scriptEngineManager; 73 /** 74 * ScriptEngine which contains the file with logic. 75 */ 76 protected ScriptEngine engine = null; 77 /** 78 * Same engine as in {@link engine}, but recasted as Invocable, allowing us to call methods. 79 */ 80 protected Invocable invocableEngine = null; 81 82 /** 83 * Constructor which is needed when you have to initialize the 84 * environment of the scripting language. 85 * <p> 86 * Currently used by SPOSHBot. 87 */ 88 @Override 89 public void initializeController(UT2004Bot bot) { 90 super.initializeController(bot); 91 // first create proper ScriptEngine 92 scriptEngineManager = createScriptEngineManager(); 93 this.engine = createScriptEngine(scriptEngineManager); 94 this.invocableEngine = (Invocable) this.engine; 95 this.engineBinded(); 96 97 // and than load up the script. 98 try { 99 evalStream(this.getScriptStream()); 100 } catch (Exception ex) { 101 ex.printStackTrace(); 102 throw new ScriptedAgentException("Unable to evaluate script", ex); 103 } 104 scriptBinded(); 105 } 106 107 /** 108 * Return correct ScriptEngine for script this bot is going to run. 109 * <p> 110 * Because this class is using stream instead of file we don't know what 111 * is scripting language of processed script. That is what this function is for. 112 * 113 * @param manager {@link ScriptEngineManager} that is used to manage engines. 114 * @return correct {@link ScriptEngine} for this agent. 115 */ 116 protected abstract ScriptEngine createScriptEngine(ScriptEngineManager manager); 117 118 /** 119 * Return stream for script that this class should execute. 120 * @return input stream containing script. Not null. 121 */ 122 protected abstract Reader getScriptStream() throws IOException; 123 124 /** 125 * This method is called after the script is evaluated. 126 * To be used by developer, for some adjustments or such. 127 */ 128 protected abstract void scriptBinded(); 129 130 /** 131 * Creates script engine manager that looks for engines in classpath and 132 * also in $POGAMUT_PLATFORM/scriptEngines. 133 */ 134 private static ScriptEngineManager createScriptEngineManager() { 135 return new ScriptEngineManager(); 136 } 137 138 /** 139 * This method is called when the engine is binded. 140 */ 141 protected abstract void engineBinded(); 142 143 /** 144 * Sets attribute to the global scope of the engine. 145 * TODO: same as engine.put(name, attribute) ? Learn more about scopes. 146 * @param name 147 * @param attribute 148 */ 149 final protected void setAttribute(String name, Object attribute) { 150 ScriptContext context = this.engine.getContext(); 151 List<Integer> scopes = context.getScopes(); 152 context.setAttribute(name, attribute, scopes.get(0)); 153 } 154 155 /** 156 * Evaluates the stream of the script. 157 * 158 * @param is 159 * @return true if eval succeeded 160 * @throws ScriptedAgentException 161 */ 162 final protected boolean evalStream(Reader reader) throws ScriptedAgentException { 163 try { 164 engine.eval(reader); 165 return true; 166 } catch (ScriptException e) { 167 getLog().severe("Script error -> " + e.getMessage()); 168 throw new ScriptedAgentException("Script error -> " + e.getMessage(), e); 169 } 170 } 171 172 /** 173 * Calls function without parameters from the ScriptEngine. 174 * 175 * @param name 176 * @return true if everything is ok 177 * @throws ScriptedAgentException 178 */ 179 final protected boolean callFunction(String name) throws ScriptedAgentException { 180 try { 181 this.invocableEngine.invokeFunction(name, (Object[]) null); 182 } catch (ScriptException e) { 183 String msg = "Script exception -> " + e.getMessage(); 184 getLog().severe(msg); 185 186 throw new ScriptedAgentException(msg, e); 187 } catch (NoSuchMethodException e) { 188 String msg = "Method '" + name + "' uninmplemented by scripts."; 189 getLog().severe(msg); 190 191 throw new ScriptedAgentException(msg, e); 192 } 193 return true; 194 } 195 196 public UT2004Bot getBot() { 197 return bot; 198 } 199 200 }