001 package toolbus.tool;
002
003 import java.util.Iterator;
004 import java.util.LinkedList;
005 import java.util.List;
006
007 import toolbus.IOperations;
008 import toolbus.TBTermFactory;
009 import toolbus.ToolBus;
010 import toolbus.ToolInstanceManager;
011 import toolbus.communication.IDataHandler;
012 import toolbus.communication.IIOHandler;
013 import toolbus.environment.Environment;
014 import toolbus.exceptions.ToolBusException;
015 import toolbus.logging.ILogger;
016 import toolbus.logging.IToolBusLoggerConstants;
017 import toolbus.logging.LoggerFactory;
018 import toolbus.tool.execution.IToolExecutor;
019 import toolbus.tool.execution.IToolExecutorFactory;
020 import toolbus.tool.execution.StreamHandler;
021 import aterm.AFun;
022 import aterm.ATerm;
023 import aterm.ATermAppl;
024
025 /**
026 * Provides the interface to a tool instance.
027 *
028 * @author Arnold Lankamp
029 */
030 public class ToolInstance implements IDataHandler, IOperations{
031 private final ToolDefinition toolDef;
032 private final ToolBus toolbus;
033 private final TBTermFactory tbfactory;
034
035 private volatile IIOHandler ioHandler = null;
036
037 private volatile StreamHandler streamHandler = null;
038
039 private static int toolNr = 0;
040
041 private final ATermAppl toolKey;
042 private final int toolID;
043
044 private final List<ATerm> valuesFromTool;
045 private final List<ATerm> eventsFromTool;
046 private final List<ATerm> requestsFromTool;
047
048 private final List<ATerm> performanceStats;
049
050 private volatile ATerm lastDebugPerformanceStats = null;
051
052 /**
053 * Constructor.
054 *
055 * @param toolDef
056 * The definition of the tool.
057 * @param toolbus
058 * A reference to the toolbus associated with this tool instance.
059 */
060 public ToolInstance(ToolDefinition toolDef, ToolBus toolbus){
061 super();
062
063 this.toolDef = toolDef;
064 this.toolbus = toolbus;
065 this.tbfactory = toolbus.getTBTermFactory();
066
067 synchronized(getClass()){
068 this.toolID = toolNr++;
069 }
070
071 AFun afun = tbfactory.makeAFun(toolDef.getName(), 1, false);
072 toolKey = tbfactory.makeAppl(afun, tbfactory.makeInt(toolID));
073
074 valuesFromTool = new LinkedList<ATerm>();
075 eventsFromTool = new LinkedList<ATerm>();
076 requestsFromTool = new LinkedList<ATerm>();
077
078 performanceStats = new LinkedList<ATerm>();
079 }
080
081 /**
082 * Executes the tool that should be associated with this tool instance.
083 *
084 * @throws ToolBusException
085 * Thrown when the tool could not be executed.
086 */
087 public void executeTool() throws ToolBusException{
088 IToolExecutorFactory toolExecutorFactory = toolbus.getToolExecutorFactory();
089
090 IToolExecutor toolExecutor = toolExecutorFactory.getToolExecutor(this, toolDef, toolbus);
091 toolExecutor.execute();
092 }
093
094 /**
095 * Returns the key of the tool that is associated with this tool instance.
096 *
097 * @return The key of the tool that is associated with this tool instance.
098 */
099 public ATermAppl getToolKey(){
100 return toolKey;
101 }
102
103 /**
104 * Returns the id of the tool that is associated with this tool instance.
105 *
106 * @return The id of the tool that is associated with this tool instance.
107 */
108 public int getToolID(){
109 return toolID;
110 }
111
112 /**
113 * Returns the name of the tool that is associated with this tool instance.
114 *
115 * @return The name of the tool that is associated with this tool instance.
116 */
117 public String getToolName(){
118 return toolKey.getName();
119 }
120
121 /**
122 * Sets the stream handler for this tool instance.
123 *
124 * @param streamHandler
125 * The stream handler to use.
126 */
127 public void setStreamHandler(StreamHandler streamHandler){
128 this.streamHandler = streamHandler;
129 }
130
131 /**
132 * @see toolbus.communication.IDataHandler#setIOHandler(IIOHandler)
133 */
134 public void setIOHandler(IIOHandler ioHandler){
135 this.ioHandler = ioHandler;
136 }
137
138 /**
139 * @see toolbus.communication.IDataHandler#receive(byte, ATerm)
140 */
141 public void receive(byte operation, ATerm aTerm){
142 switch(operation){
143 case EVENT:
144 synchronized(eventsFromTool){
145 eventsFromTool.add(aTerm);
146 }
147 break;
148 case REQUEST:
149 synchronized(requestsFromTool){
150 requestsFromTool.add(aTerm);
151 }
152 break;
153 case ACKDO:
154 goReady();
155 break;
156 case VALUE:
157 goReady();
158 synchronized(valuesFromTool){
159 valuesFromTool.add(aTerm);
160 }
161 break;
162 case DISCONNECT:
163 goDisconnected();
164 break;
165 case CONNECT:
166 goConnected();
167 break;
168 case PERFORMANCESTATS:
169 goReady();
170 performanceStats.add(aTerm);
171 break;
172 case DEBUGPERFORMANCESTATS:
173 lastDebugPerformanceStats = aTerm;
174 break;
175 default:
176 LoggerFactory.log("Message with unknown operation received from Tool: " + operation, ILogger.WARNING, IToolBusLoggerConstants.TOOLINSTANCE);
177 }
178
179 // Notify the ToolBus of the arrival of a message.
180 toolbus.workArrived(this, operation);
181 }
182
183 /**
184 * Sends an ack event to the with this tool instance associated tool.
185 *
186 * @param aTerm
187 * The ack event.
188 */
189 public void sendAckEvent(ATerm aTerm){
190 send(ACKEVENT, aTerm);
191 }
192
193 /**
194 * Sends an response to the with this tool instance associated tool.
195 *
196 * @param aTerm
197 * The response.
198 */
199 public void sendResponse(ATerm aTerm){
200 send(RESPONSE, aTerm);
201 }
202
203 /**
204 * Sends a do request to the with this tool instance associated tool.
205 *
206 * @param aTerm
207 * The do request.
208 */
209 public void sendDo(ATerm aTerm){
210 send(DO, aTerm);
211 }
212
213 /**
214 * Sends an evaluation request to the with this tool instance associated tool.
215 *
216 * @param aTerm
217 * The evaluation request.
218 */
219 public void sendEval(ATerm aTerm){
220 send(EVAL, aTerm);
221 }
222
223 /**
224 * Sends a termination request to the with this tool instance associated tool.
225 *
226 * @param aTerm
227 * The termination request.
228 */
229 public void sendTerminate(ATerm aTerm){
230 send(TERMINATE, aTerm);
231 }
232
233 /**
234 * Sends a performance statistics request to the with this tool instance associated tool.
235 *
236 * @param aTerm
237 * The performance statistics request.
238 */
239 public void sendPerformanceStatsRequest(ATerm aTerm){
240 send(PERFORMANCESTATS, aTerm);
241 }
242
243 /**
244 * Sends a debug performance statistics request to the with this tool instance associated tool.
245 */
246 public void sendDebugPerformanceStatsRequest(){
247 send(DEBUGPERFORMANCESTATS, toolbus.getTBTermFactory().makeList());
248 }
249
250 /**
251 * @see toolbus.communication.IDataHandler#send(byte, ATerm)
252 */
253 public void send(byte operation, ATerm aTerm){
254 ioHandler.send(operation, aTerm);
255 }
256
257 /**
258 * @see toolbus.communication.IDataHandler#terminate()
259 */
260 public void terminate(){
261 ioHandler.terminate();
262 }
263
264 /**
265 * @see toolbus.communication.IDataHandler#shutDown()
266 */
267 public void shutDown(){
268 boolean connected = isConnected();
269
270 goTerminated();
271
272 ToolInstanceManager tim = toolbus.getToolInstanceManager();
273 if(connected){ // If the tool is connected we only need to check in one collection.
274 tim.remove(toolKey);
275 }else{
276 tim.remove(toolKey);
277 tim.removePendingTool(toolKey);
278 tim.removeDynamiclyConnectedTool(this);
279 }
280
281 toolbus.workArrived(this, IOperations.END);
282 }
283
284 /**
285 * Checks if the tool that is associated with this tool instance was executed or connected on it's own initiative.
286 *
287 * @return True if the tool was executed; false otherwise.
288 */
289 public boolean isExecutedTool(){
290 return (streamHandler != null);
291 }
292
293 /**
294 * Forcefully terminates the with this tool instance associated tool.
295 * NOTE: This only works for executed tools. Tools that connected on their own initiative won't
296 * be killed by the invocation of this method; however they will become unreachable and their
297 * state will be set to 'killed'.
298 */
299 public void kill(){
300 boolean connected = isConnected();
301
302 goKilled();
303
304 ToolInstanceManager tim = toolbus.getToolInstanceManager();
305 if(connected){ // If the tool is connected we only need to check in one collection.
306 tim.remove(toolKey);
307 }else{
308 tim.remove(toolKey);
309 tim.removePendingTool(toolKey);
310 tim.removeDynamiclyConnectedTool(this);
311 }
312
313 if(streamHandler != null) streamHandler.destroy(); // Forcefully kills the tool.
314 }
315
316 /**
317 * @see IDataHandler#exceptionOccured()
318 */
319 public void exceptionOccured(){
320 goUnreachable();
321
322 LoggerFactory.log("Tool crashed / disconnected: "+toolKey, ILogger.WARNING, IToolBusLoggerConstants.TOOLINSTANCE);
323 }
324
325 /**
326 * Attempts to find a value that matches the given signature. If this is the case the
327 * environment will be updated.
328 *
329 * @param pattern
330 * The signature we need to match on.
331 * @param env
332 * The enviroment in which we need to make updates, when a match have been made.
333 * @return Indicates if the matches was successful.
334 */
335 public boolean getValueFromTool(ATerm pattern, Environment env){
336 synchronized(valuesFromTool){
337 Iterator<ATerm> valuesIterator = valuesFromTool.iterator();
338 while(valuesIterator.hasNext()){
339 boolean matches = tbfactory.matchPatternToValue(pattern, env, valuesIterator.next());
340 if(matches){
341 valuesIterator.remove();
342 return true;
343 }
344 }
345 }
346 return false;
347 }
348
349 /**
350 * Attempts to find a event that matches the given signature. If one is found the environment
351 * will be updated.
352 *
353 * @param pattern
354 * The signature we need to match on.
355 * @param env
356 * The enviroment in which we need to make updates, when a match have been made.
357 * @return Indicates if the matches was successful.
358 */
359 public boolean getEventFromTool(ATerm pattern, Environment env){
360 synchronized(eventsFromTool){
361 Iterator<ATerm> eventsIterator = eventsFromTool.iterator();
362 while(eventsIterator.hasNext()){
363 boolean matches = tbfactory.matchPatternToValue(pattern, env, eventsIterator.next());
364 if(matches){
365 eventsIterator.remove();
366 return true;
367 }
368 }
369 }
370 return false;
371 }
372
373 /**
374 * Attempts to find a request that matches the given signature. If one is found the environment
375 * will be updated.
376 *
377 * @param pattern
378 * The signature we need to match on.
379 * @param env
380 * The enviroment in which we need to make updates, when a match have been made.
381 * @return Indicates if the matches was successful.
382 */
383 public boolean getRequestFromTool(ATerm pattern, Environment env){
384 synchronized(requestsFromTool){
385 Iterator<ATerm> requestsIterator = requestsFromTool.iterator();
386 while(requestsIterator.hasNext()){
387 boolean matches = tbfactory.matchPatternToValue(pattern, env, requestsIterator.next());
388 if(matches){
389 requestsIterator.remove();
390 return true;
391 }
392 }
393 }
394 return false;
395 }
396
397 /**
398 * Attempts to find performance statistics that match the given signature. If one is found the
399 * environment will be updated.
400 *
401 * @param aTerm
402 * The signature we need to match on.
403 * @param env
404 * The enviroment in which we need to make updates, when a match have been made.
405 * @return Indicates if the matches was successful.
406 */
407 public boolean getPerformanceStats(ATerm aTerm, Environment env){
408 synchronized(performanceStats){
409 Iterator<ATerm> performanceStatsIterator = performanceStats.iterator();
410 while(performanceStatsIterator.hasNext()){
411 boolean matches = tbfactory.matchPatternToValue(aTerm, env, performanceStatsIterator.next());
412 if(matches){
413 performanceStatsIterator.remove();
414 return true;
415 }
416 }
417 }
418 return false;
419 }
420
421 /**
422 * Returns the last received batch of debug performance statistics.
423 * NOTE: This method will only be used in debug mode, by the debug ToolBus.
424 *
425 * @return The last received batch of debug performance statistics.
426 */
427 public ATerm getLastDebugPerformanceStats(){
428 return lastDebugPerformanceStats;
429 }
430
431 private final static int UNCONNECTED = 0; // Start and end state.
432 private final static int READY = 2; // Ready to send (DO or EVAL).
433 private final static int BLOCKED = 3; // Waiting for response (ACKDO or VALUE).
434 private final static int DISCONNECTED = 4; // Termination requested by tool.
435 private final static int KILLED = 5; // User imposed error state.
436 private final static int UNREACHABLE = 6; // Error state.
437 private final static int TERMINATED = 7; // End state.
438
439 private volatile int state = UNCONNECTED;
440
441 private final Object stateLock = new Object();
442
443 /**
444 * Notifies this tool instance that the tool connected.
445 */
446 private void goConnected(){
447 state = READY;
448 }
449
450 /**
451 * Notifies this tool instance that the tool is ready to receive a new DO or EVAL request.
452 */
453 public void goReady(){
454 synchronized(stateLock){
455 if(state == BLOCKED) state = READY; // Only go back to ready when we're still reachable (i.e. not killed/terminated/disconnected).
456 }
457 }
458
459 /**
460 * Notifies this tool instance that the tool has disconnected.
461 */
462 private void goDisconnected(){
463 synchronized(stateLock){
464 if(isConnected()) state = DISCONNECTED; // Only go back to disconnected when we're still reachable (i.e. not killed/terminated).
465 }
466 }
467
468 /**
469 * Notifies this tool instance that the tool has terminated.
470 */
471 private void goTerminated(){
472 state = TERMINATED;
473 }
474
475 /**
476 * Notifies this tool instance that the tool has been killed.
477 */
478 private void goKilled(){
479 state = KILLED;
480 }
481
482 /**
483 * Notifies this tool instance that the tool has become unreachable.
484 */
485 private void goUnreachable(){
486 state = UNREACHABLE;
487 }
488
489 /**
490 * Checks if the tool is connected.
491 *
492 * @return True if the tool is connected; false otherwise.
493 */
494 public boolean isConnected(){
495 return ((state & (READY | BLOCKED)) != 0); // We can do something smart here (0x2 indicates that we're connected since the ready and blocked states are the only two states for which this bit is set).
496 }
497
498 /**
499 * Checks if the tool is ready.
500 *
501 * @return True if the tool is ready; false otherwise.
502 */
503 public boolean isReady(){
504 return (state == READY);
505 }
506
507 /**
508 * Checks if the tool has disconnected.
509 *
510 * @return True if the tool has disconnected; false otherwise.
511 */
512 public boolean isDisconnected(){
513 return (state == DISCONNECTED);
514 }
515
516 /**
517 * Checks if the tool has been terminated.
518 *
519 * @return True if the tool has been terminated.
520 */
521 public boolean isTerminated(){
522 return (state == TERMINATED);
523 }
524
525 /**
526 * Checks if the tool has been killed.
527 *
528 * @return True if the tool has been killed; false otherwise.
529 */
530 public boolean isKilled(){
531 return (state == KILLED);
532 }
533
534 /**
535 * Checks if the tool has become unreachable.
536 *
537 * @return True if the tool has become unreachable.
538 */
539 public boolean isUnreachable(){
540 return (state == UNREACHABLE);
541 }
542
543 /**
544 * Notifies the tool instance that you want to send a DO or EVAL request to the tool and checks
545 * if this is possible.
546 *
547 * @return True if it is possible to send a DO or EVAL request; false otherwise.
548 */
549 public boolean tryDoEval(){
550 synchronized(stateLock){
551 if(state == READY){
552 state = BLOCKED;
553 return true;
554 }
555 }
556 return false;
557 }
558
559 /**
560 * Returns an array of all queued rec-value terms for this tool instance.
561 *
562 * @return An array of all queued rec-value terms for this tool instance.
563 */
564 public ATerm[] getQueuedValues(){
565 synchronized(valuesFromTool){
566 ATerm[] queuedValues = new ATerm[valuesFromTool.size()];
567 valuesFromTool.toArray(queuedValues);
568 return queuedValues;
569 }
570 }
571
572 /**
573 * Returns an array of all queued rec-event terms for this tool instance.
574 *
575 * @return An array of all queued rec-event terms for this tool instance.
576 */
577 public ATerm[] getQueuedEvents(){
578 synchronized(eventsFromTool){
579 ATerm[] queuedEvents = new ATerm[eventsFromTool.size()];
580 eventsFromTool.toArray(queuedEvents);
581 return queuedEvents;
582 }
583 }
584
585 /**
586 * Returns an array of all queued rec-request terms for this tool instance.
587 *
588 * @return An array of all queued rec-request terms for this tool instance.
589 */
590 public ATerm[] getQueuedRequests(){
591 synchronized(requestsFromTool){
592 ATerm[] queuedRequests = new ATerm[requestsFromTool.size()];
593 requestsFromTool.toArray(queuedRequests);
594 return queuedRequests;
595 }
596 }
597
598 /**
599 * Returns the status of this tool instance in string format.
600 *
601 * @return The status of this tool instance in string format.
602 */
603 public String showStatus(){
604 synchronized(stateLock){
605 return "tool " + toolKey.toString() + "(state = " + state + ")";
606 }
607 }
608 }