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 }