001 package toolbus.adapter.wish; 002 003 import java.io.BufferedReader; 004 import java.io.IOException; 005 import java.io.InputStream; 006 import java.io.InputStreamReader; 007 import java.io.OutputStream; 008 import java.net.InetAddress; 009 import java.util.ArrayList; 010 import java.util.List; 011 012 import toolbus.adapter.AbstractTool; 013 import aterm.AFun; 014 import aterm.ATerm; 015 import aterm.ATermAppl; 016 import aterm.ATermList; 017 import aterm.pure.PureFactory; 018 019 public class WishAdapter extends AbstractTool{ 020 private final static String WISH_COMMAND = "wish"; 021 022 private String scriptName = null; 023 private String tbtcl = null; 024 private String libdir = null; 025 private final List<String> arguments; 026 027 private Process process = null; 028 private OutputStream wishInputStream = null; 029 private OutputStreamHandler outputStreamHandler = null; 030 private ErrorStreamHandler errorStreamHandler = null; 031 032 private final byte[] spaceBytes; 033 private final byte[] startCallBytes; 034 private final byte[] endCallBytes; 035 private final byte[] startAckEventBytes; 036 private final byte[] endAckEventBytes; 037 private final byte[] startTerminateBytes; 038 private final byte[] endTerminateBytes; 039 040 private final Object valueLock = new Object(); 041 private ATerm value = null; 042 043 public WishAdapter(){ 044 super(); 045 046 arguments = new ArrayList<String>(); 047 048 spaceBytes = " ".getBytes(); 049 startCallBytes = "if [catch {".getBytes(); 050 endCallBytes = "} msg] {TBerror $msg}\n".getBytes(); 051 startAckEventBytes = "rec-ack-event {".getBytes(); 052 endAckEventBytes = "}".getBytes(); 053 startTerminateBytes = "rec-terminate {".getBytes(); 054 endTerminateBytes = "}".getBytes(); 055 } 056 057 public void connect(String[] args) throws Exception{ 058 String toolName = null; 059 int toolID = -1; 060 061 InetAddress host = null; 062 int port = -1; 063 064 arguments.clear(); // Insurance, this isn't strictly needed. 065 066 for(int i = 0; i < args.length; i++){ 067 String arg = args[i]; 068 if(arg.equals("-TB_TOOL_NAME")){ 069 toolName = args[++i]; 070 }else if(arg.equals("-TB_TOOL_ID")){ 071 toolID = Integer.parseInt(args[++i]); 072 }else if(arg.equals("-TB_HOST")){ 073 host = InetAddress.getByName(args[++i]); 074 }else if(arg.equals("-TB_PORT")){ 075 port = Integer.parseInt(args[++i]); 076 }else if(arg.equals("-script")){ 077 scriptName = args[++i]; 078 }else if(arg.equals("-tbtcl")){ 079 tbtcl = args[++i]; 080 }else if(arg.equals("-libdir")){ 081 libdir = args[++i]; 082 }else if(arg.equals("-script-args")){ 083 while(++i < args.length){ 084 arguments.add(args[i]); 085 } 086 }else{ 087 throw new RuntimeException("Unknown argument: " + arg); 088 } 089 } 090 091 if(toolName == null) throw new RuntimeException("Missing tool identification."); 092 if(scriptName == null) throw new RuntimeException("No script name supplied."); 093 if(tbtcl == null || libdir == null) throw new RuntimeException("No library paths supplied."); 094 095 toolBridge = new WishAdapterBridge(getFactory(), this, toolName, toolID, host, port); 096 097 executeScript(); 098 099 toolBridge.run(); 100 101 startHandlingIO(); 102 103 int exitCode = process.waitFor(); 104 if(exitCode != 0){ 105 System.err.println("Script exited with error code: " + exitCode); 106 System.exit(exitCode); 107 } 108 } 109 110 private void executeScript() throws IOException{ 111 String[] command = new String[3]; 112 command[0] = WISH_COMMAND; 113 command[1] = "-name"; 114 command[2] = getToolBridge().getToolName(); 115 116 ProcessBuilder pb = new ProcessBuilder(command); 117 118 process = pb.start(); 119 120 wishInputStream = process.getOutputStream(); 121 outputStreamHandler = new OutputStreamHandler(process.getInputStream()); 122 errorStreamHandler = new ErrorStreamHandler(process.getErrorStream()); 123 124 initWish(); 125 } 126 127 private void initWish() throws IOException{ 128 wishInputStream.write("source ".getBytes()); 129 wishInputStream.write(tbtcl.getBytes()); 130 wishInputStream.write("\n".getBytes()); 131 132 wishInputStream.write("set argv { ".getBytes()); 133 int nrOfArguments = arguments.size(); 134 int i = 0; 135 while(i < nrOfArguments){ 136 wishInputStream.write(arguments.get(i++).getBytes()); 137 wishInputStream.write(spaceBytes); 138 } 139 wishInputStream.write("}\n".getBytes()); 140 141 wishInputStream.write("set argc ".getBytes()); 142 wishInputStream.write(("" + nrOfArguments).getBytes()); 143 wishInputStream.write("\n".getBytes()); 144 145 wishInputStream.write("set TB_LIBDIR ".getBytes()); 146 wishInputStream.write(libdir.getBytes()); 147 wishInputStream.write("\n".getBytes()); 148 149 wishInputStream.write("source ".getBytes()); 150 wishInputStream.write(scriptName.getBytes()); 151 wishInputStream.write("\n".getBytes()); 152 153 wishInputStream.flush(); 154 } 155 156 private void startHandlingIO(){ 157 Thread outputStreamHandlerThread = new Thread(outputStreamHandler); 158 outputStreamHandlerThread.setName("Output stream handler"); 159 outputStreamHandlerThread.setDaemon(true); 160 outputStreamHandlerThread.start(); 161 162 Thread errorStreamHandlerThread = new Thread(errorStreamHandler); 163 errorStreamHandlerThread.setName("Error stream handler"); 164 errorStreamHandlerThread.setDaemon(true); 165 errorStreamHandlerThread.start(); 166 } 167 168 protected void valueReady(ATerm v){ 169 value = v; 170 synchronized(valueLock){ 171 valueLock.notify(); 172 } 173 } 174 175 private void dumpArgument(ATerm arg) throws IOException{ 176 if(arg.getType() == ATerm.LIST){ 177 dumpListArgument((ATermList) arg); 178 }else{ 179 wishInputStream.write(arg.toString().getBytes()); 180 } 181 } 182 183 private void dumpListArgument(ATermList list) throws IOException{ 184 if(list.isEmpty()){ 185 wishInputStream.write("{}".getBytes()); 186 }else{ 187 wishInputStream.write("{".getBytes()); 188 while(!list.isEmpty()){ 189 ATerm head = list.getFirst(); 190 dumpArgument(head); 191 list = list.getNext(); 192 if(!list.isEmpty()){ 193 wishInputStream.write(spaceBytes); 194 } 195 } 196 wishInputStream.write("}".getBytes()); 197 } 198 } 199 200 private void dumpReceivedTerm(ATerm aTerm){ 201 ATermAppl doTerm = (ATermAppl) aTerm; 202 AFun fun = doTerm.getAFun(); 203 String functionName = fun.getName(); 204 ATerm[] arguments = doTerm.getArgumentArray(); 205 int nrOfArguments = arguments.length; 206 207 try{ 208 wishInputStream.write(startCallBytes); 209 wishInputStream.write(functionName.getBytes()); 210 wishInputStream.write(spaceBytes); 211 int i = 0; 212 while(i < nrOfArguments){ 213 dumpArgument(arguments[i++]); 214 wishInputStream.write(spaceBytes); 215 } 216 wishInputStream.write(endCallBytes); 217 218 wishInputStream.flush(); 219 }catch(IOException ioex){ 220 ioex.printStackTrace(); 221 System.err.println("Something went terribly wrong with the TCL/TK tool. Committing suicide now ...."); 222 System.exit(0); // Kill yourself. 223 } 224 } 225 226 public void receiveDo(ATerm aTerm){ 227 dumpReceivedTerm(aTerm); 228 } 229 230 public ATerm receiveEval(ATerm aTerm){ 231 dumpReceivedTerm(aTerm); 232 // Receive the value. 233 synchronized (valueLock){ 234 while(value == null){ 235 try{ 236 valueLock.wait(); 237 }catch(InterruptedException irex){ 238 // Ignore this exception. 239 } 240 } 241 } 242 243 ATerm result = value; 244 value = null; // Reset the value field. 245 246 return result; 247 } 248 249 public void receiveTerminate(ATerm aTerm){ 250 try{ 251 wishInputStream.write(startCallBytes); 252 wishInputStream.write(startTerminateBytes); 253 wishInputStream.write(spaceBytes); 254 255 wishInputStream.write(aTerm.toString().getBytes()); 256 wishInputStream.write(spaceBytes); 257 258 wishInputStream.write(endTerminateBytes); 259 wishInputStream.write(endCallBytes); 260 261 wishInputStream.flush(); 262 }catch(IOException ioex){ 263 ioex.printStackTrace(); 264 System.err.println("Something went terribly wrong with the TCL/TK tool. Committing suicide now ...."); 265 System.exit(0); // Kill yourself. 266 } 267 } 268 269 public void receiveAckEvent(ATerm aTerm){ 270 ATermList ackCallbackData = (ATermList) aTerm; 271 272 try{ 273 wishInputStream.write(startCallBytes); 274 wishInputStream.write(startAckEventBytes); 275 wishInputStream.write(spaceBytes); 276 277 ATermList empty = getFactory().makeList(); 278 while(ackCallbackData != empty){ 279 ATermList next = ackCallbackData.getNext(); 280 ATerm first = ackCallbackData.getFirst(); 281 282 wishInputStream.write(first.toString().getBytes()); 283 wishInputStream.write(spaceBytes); 284 285 ackCallbackData = next; 286 } 287 wishInputStream.write(endAckEventBytes); 288 wishInputStream.write(endCallBytes); 289 290 wishInputStream.flush(); 291 }catch(IOException ioex){ 292 ioex.printStackTrace(); 293 System.err.println("Something went terribly wrong with the TCL/TK tool. Committing suicide now ...."); 294 System.exit(0); // Kill yourself. 295 } 296 } 297 298 private class OutputStreamHandler implements Runnable{ 299 private final InputStream inputStream; 300 301 public OutputStreamHandler(InputStream inputStream){ 302 this.inputStream = inputStream; 303 } 304 305 public void run(){ 306 BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); 307 PureFactory factory = getFactory(); 308 309 String line = null; 310 try{ 311 while((line = reader.readLine()) != null){ 312 ATermAppl term = (ATermAppl) factory.parse(line); 313 AFun fun = term.getAFun(); 314 String operation = fun.getName(); 315 316 // The operation has already been interned, so match 317 // pointers instead of using equals. 318 if(operation == "snd-event"){ 319 ATerm sndEventTerm = term.getArgument(0); 320 sendEvent(sndEventTerm); 321 }else if(operation == "snd-value"){ 322 ATerm valueTerm = term.getArgument(0); 323 valueReady(valueTerm); 324 }else if(operation == "snd-disconnect"){ 325 disconnect(factory.makeList()); 326 } 327 } 328 }catch(IOException ioex){ 329 ioex.printStackTrace(); 330 }catch(RuntimeException rex){ 331 rex.printStackTrace(); 332 }finally{ 333 // Close the reader. 334 try{ 335 reader.close(); 336 }catch(IOException ioex){ 337 ioex.printStackTrace(); 338 } 339 } 340 } 341 } 342 343 private class ErrorStreamHandler implements Runnable{ 344 private final InputStream inputStream; 345 346 public ErrorStreamHandler(InputStream inputStream){ 347 this.inputStream = inputStream; 348 } 349 350 public void run(){ 351 BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); 352 353 String line = null; 354 try{ 355 while((line = reader.readLine()) != null){ 356 System.err.println(line); 357 } 358 }catch(IOException ioex){ 359 ioex.printStackTrace(); 360 }catch(RuntimeException rex){ 361 rex.printStackTrace(); 362 }finally{ 363 // Close the reader. 364 try{ 365 reader.close(); 366 }catch(IOException ioex){ 367 ioex.printStackTrace(); 368 } 369 } 370 } 371 } 372 373 public static void main(String[] args) throws Exception{ 374 WishAdapter wishAdapter = new WishAdapter(); 375 wishAdapter.connect(args); 376 } 377 }