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 }