1// BEGIN LICENSE BLOCK 2// Version: CMPL 1.1 3// 4// The contents of this file are subject to the Cisco-style Mozilla Public 5// License Version 1.1 (the "License"); you may not use this file except 6// in compliance with the License. You may obtain a copy of the License 7// at www.eclipse-clp.org/license. 8// 9// Software distributed under the License is distributed on an "AS IS" 10// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 11// the License for the specific language governing rights and limitations 12// under the License. 13// 14// The Original Code is The ECLiPSe Constraint Logic Programming System. 15// The Initial Developer of the Original Code is Cisco Systems, Inc. 16// Portions created by the Initial Developer are 17// Copyright (C) 2006 Cisco Systems, Inc. All Rights Reserved. 18// 19// Contributor(s): 20// 21// END LICENSE BLOCK 22 23package com.parctechnologies.eclipse.visualisation; 24 25import com.parctechnologies.eclipse.*; 26import java.io.*; 27import java.net.SocketException; 28import java.util.*; 29import java.awt.event.*; 30import javax.swing.*; 31import java.beans.*; 32import java.lang.reflect.*; 33/** 34 * This is the top-level class of the Visualisation architecture on the 35 * Java side.<p> 36 * 37 * It is responsible for:<p> 38 * 1 intialising the visualisation client's streams (in this case queues between 39 * Java and Eclipse).<p> 40 * 2 Registering the VC with eclipse.<p> 41 * 3 Fulfilling the VC side of the visualisation protocol<p> 42 * 4 Maintaining a VisClientStateModel instance, which reflects the current state 43 * that the VisClient is in.<p> 44 * 5 Unregistering the visualisation client and cleaning up the Java side, when 45 * termination occurs.<p> 46 * 6 Handling any exceptions which arise from communicating with ECLiPSe.<p> 47 * 7 Participating in any Multitaksing phases that ECLiPSe enters. 48 * 49 * (1) and (2) are done by the constructor, which assumes that the calling Java 50 * thread has control (rather than Eclipse or some other Java thread) and can 51 * therefore invoke methods such as rpc on the EclipseConnection.<p> 52 * 53 * (3) and (4) work as follows:<p> 54 * 55 * There are two QueueListeners on the two fromEclipse queues (viewables 56 * and updates). These are responsible for dealing with the various messages 57 * which appear according to the visualisation protocol on these queues.<p> 58 * 59 * The basic way this works is to <p> 60 * 61 * a) "parse" the message, turning it from an EXDR 62 * structure into a VisEvent object.<p> 63 * 64 * b) "process" the event. This mainly involves calling the processEvent method, 65 * but there are also various other things to do based on the event type, for 66 * example getting the new size of the viewable in the case of 67 * create/expand/contract events. <p> 68 * 69 * processEvent is really the top-level method which is invoked when a 70 * visualisation event happens. For each event that happens, the VisClient 71 * progresses through a common sequence of states. The processEvent method 72 * "directs" the state sequence of the 73 * visualisation client throughout the duration of the VisEvent. However, the 74 * state itself is encapsulated in the VisClientStateModel, which is passed by 75 * reference to various other objects which want to be aware of changes to the 76 * state. <p> 77 * 78 * The state progression for the VC can be seen from the structure of 79 * processEvent. During the event, most of the actual Viewer-related work 80 * relating to the changes of state is delegated to a ViewerManager object, 81 * which can see the state via the VisClientStateModel. Each stage of the state 82 * progression has a corresponding 83 * static int constant in VisClientStateModel. Transitions between states 84 * correspond to invocations of methods in ViewerManager 85 * <p> 86 * <IMG src="doc-files/VisClient-1.png" ALT="state transition diagram"></IMG> 87 * <p> 88 * <ul> 89 * <li> start off in state NO_CURRENT_EVENT 90 * <li> message arrives on a queue, VC now has control, go to state 91 * SETTING_VIEWER_POLICY 92 * <li> If the event is a CREATE_EVENT 93 * <ul> 94 * <li> User is given option to selected a saved Scenario for playback 95 * <li> ViewerManager.configureViewerBuildingPolicy is called to initialise 96 * the Viewers for this viewable 97 * </ul> 98 * </li> 99 * <li> message arrives on a queue, VC now has control, go to state 100 * COLLECTING_PRE_EVENT_GOALS. 101 * <li> ViewerManager.prepareForEvent is called 102 * <li> collect the preEvent goals from the ViewerManager 103 * <li> Eclipse has control, move to state EXECUTING_PRE_EVENT_GOALS 104 * <li> execute the goals 105 * <li> VC has control again 106 * <li> distribute the goal results to the viewerManager 107 * <li> call ViewerManager.startEvent 108 * <li> if we want to hold for this event, set a flag on the ECLiPSe side by means of an RPC (this flag will cause ECLiPSe to hit a breakpoint immediately control is returned to it), 109 * otherwise go directly to state EVENT_IS_FINISHED. 110 * <li> call ViewerManager.stopEvent 111 * <li> return to state NO_CURRENT_EVENT 112 * <li> ECLiPSe now has control again 113 * <li> If we had set the breakpoint (indicating that we want to hold) then 114 * the state is set to HELD_ON_EVENT and a Multitaksing phase will begin. 115 * <li> As ECLiPSe polls registered peers during the multitask phase, the 116 * VC will recieve multitask messages through the VisMultitaskListener. 117 * When the multitask phase starts, the 118 * state flag "multitaskPhase" is set to true (thus enabling the resume 119 * button) and any RPCs which need to be performed can be done so (eg. 120 * Changes to the update granularity, or pre-build goals for new Viewlets). 121 * <li>Should another peer end the multitasking phase or should the Resume 122 * action be performed (by pressing the resume button) the stopEvent() 123 * method is called which sets the state back to NO_CURRENT_EVENT. 124 * </ul> 125 * 126 * 127 * (5): termination can happen due to a user event when Eclipse has control. This 128 * is handled by the setTerminate method within VisClientStateModel. Termination 129 * can also happen when the VC has control, either due to a user event or when a 130 * specific "terminate" message has been received from ECLiPSe. In either of 131 * these cases the terminate method is invoked, which does the necessary 132 * cleaning up.<p> 133 * 134 * (6): since almost all communication with the EclipseConnection is via this class, 135 * it handles exceptions arising from lost connections, terminated Eclipses 136 * or violations of the visualisation protocol using some generic recovery methods .<p> 137 * 138 * The final stage of termination is performed by the exitNormal method in the 139 * case where the VisClient meant to exit, and exitError if we are exiting as a 140 * result of an exception being thrown. 141 * 142 * (7): Consists of 143 * <ol> 144 * <li>Enabling the 'resume' button whenever a multitaksing phase is started</li> 145 * <li>Setting visualisation breakpoints whenever the VC wishes to 'hold'. 146 * </ol> 147 * 148 * 149 * 150 */ 151public class VisClient 152{ 153 154 protected VisClientStateModel stateModel; 155 private FromEclipseQueue viewables_stream; 156 private String viewables_stream_name; 157 private FromEclipseQueue updates_stream; 158 private String updates_stream_name; 159 private ToEclipseQueue interest_stream; 160 private String interest_stream_name; 161 private EXDROutputStream interest_stream_f; 162 protected Atom clientName; 163 164 protected static final String MULTITASK_PHASE_TYPE = "vis_event" ; 165 166 private static final Atom terminateAtom = new Atom("terminate"); 167 private static final Atom visProtocolSupportedAtom = 168 new Atom("vis_protocol_supported"); 169 private static final Atom vcSupportAtom = new Atom("vc_support"); 170 private static final Atom viewGranularityAtom = 171 new Atom("view_granularity"); 172 173 private VisClient visClient; 174 private ViewerManager viewerManager; 175 176 private static final int SUPPORTED_PROTOCOL_VERSION = 1; 177 178 private boolean protocol_version_supported; 179 180 181 private EclipseMultitaskConnection eclipse; 182 183 private VisEvent currentEvent; 184 185 /** 186 * Set up a java VC based on EclipseConnection eclipse. Assumes that the 187 * thread calling this constructor has access to rpc etc on the 188 * EclipseConnection (i.e. the eclipse does not have control and is not 189 * "owned" by some other java thread). 190 */ 191 public VisClient(EclipseMultitaskConnection eclipse) 192 { 193 this.eclipse = eclipse; 194 //DebuggingSupport.logMessage(this,"VisClient called with "+eclipse); 195 try 196 { 197 initialise(); 198 } 199 catch(EclipseException ee){recover_ee(ee);} 200 catch(IOException ioe){recover_ioe(ioe);} 201 catch(VisException ve){recover_ve(ve);} 202 } 203 204 private void processEvent(VisEvent visEvent) 205 { 206 // store event for stopEvent method 207 currentEvent = visEvent; 208 209 if (DebuggingSupport.logMessages) { 210 DebuggingSupport.logMessage(this, visEvent); 211 } 212 213 stateModel.setEclipseHasControl(false); 214 215 String viewableName = visEvent.getViewableName(); 216 217 stateModel.setCurrentState(VisClientStateModel.SETTING_VIEWER_POLICY); 218 if (visEvent instanceof CreateEvent) { 219 ScenarioManager.getInstance().selectPlaybackScenario(viewableName); 220 // Clear the policy flag so that the user will be prompted 221 // unless the scenario that they selected (if any) sets it 222 // as a result of the CREATE_EVENT 223 stateModel.setViewerBuildingPolicySelected(false); 224 } 225 // Hand the event off to the scenario manager which will either record 226 // the event or replay some commands or both 227 ScenarioManager.getInstance().processEvent(visEvent); 228 if (visEvent instanceof CreateEvent) { 229 // Incase the scenario does not set the policy, or if there is no 230 // scenario being played back, ask the user 231 viewerManager.configureViewerBuildingPolicy(viewableName, 232 ((CreateEvent)visEvent).getViewableType()); 233 } 234 235 236 stateModel.setCurrentState(VisClientStateModel.COLLECTING_PRE_EVENT_GOALS); 237 238 239 // Hand the event off to the scenario manager which will either record 240 // the event or replay some commands or both 241 ScenarioManager.getInstance().processEvent(visEvent); 242 243 viewerManager.prepareForEvent(visEvent); 244 245 BatchGoal preEventGoals = 246 viewerManager.collectPreEventGoals(visEvent); 247 248 List goalResults = null; 249 250 try 251 { 252 stateModel.setCurrentState(VisClientStateModel.EXECUTING_PRE_EVENT_GOALS); 253 254 // Hand the event off to the scenario manager which will either record 255 // the event or replay some commands or both 256 ScenarioManager.getInstance().processEvent(visEvent); 257 258 stateModel.setEclipseHasControl(true); 259 goalResults = preEventGoals.execute(eclipse); 260 stateModel.setEclipseHasControl(false); 261 } 262 catch(EclipseException ee){recover_ee(ee);} 263 catch(IOException ioe){recover_ioe(ioe);} 264 265 stateModel. 266 setCurrentState(VisClientStateModel.DISTRIBUTING_PRE_EVENT_GOAL_RESULTS); 267 viewerManager.startEvent(visEvent, goalResults); 268 269 stateModel.setAllScenarioCommandsExecuted(false); 270 271 // Hand the event off to the scenario manager which will either record 272 // the event or replay some commands or both 273 ScenarioManager.getInstance().processEvent(visEvent); 274 275 if (DebuggingSupport.logMessages) { 276 DebuggingSupport.logMessage(this,"invokeAndWait returns"); 277 } 278 279 280 if((viewerManager.shouldHold() || stateModel.getInterrupt()) 281 && 282 !stateModel.getTerminate()) 283 { 284 // Add to the swing queue a Runnable which signifies that all commands 285 // have now been executed. 286 try { 287 SwingUtilities.invokeAndWait(new AllCommandsExecuted()); 288 } catch(InvocationTargetException ite) { 289 ite.printStackTrace(); 290 } catch(InterruptedException ie) { 291 ie.printStackTrace(); 292 } 293 294 // trigger a breakpoint 295 stateModel.setCurrentState(VisClientStateModel.SETTING_BREAKPOINT); 296 try { 297 if (DebuggingSupport.logMessages) { 298 DebuggingSupport.logMessage(this,"Setting breakpoint"); 299 } 300 eclipse.rpc(":",vcSupportAtom,new Atom("vis_client_breakpoint")); 301 } catch(IOException ioe) { 302 recover_ioe(ioe); 303 } catch(EclipseException ee) { 304 recover_ee(ee); 305 } finally { 306 // Set the information line to the name of this event 307 viewerManager.holdingEvent(visEvent); 308 } 309 stateModel.setCurrentState(VisClientStateModel.HELD_ON_EVENT); 310 // Hand the event off to the scenario manager which will either record 311 // the event or replay some commands or both 312 ScenarioManager.getInstance().processEvent(visEvent); 313 } else { 314 stopEvent(); 315 } 316 synchronized(stateModel) { 317 // Must be carefull here to ensure that any asyncrhronous 318 // setTerminate(true) calls are handled properly 319 if(stateModel.getTerminate()) { 320 terminate(); 321 } 322 stateModel.setEclipseHasControl(true); 323 } 324 } 325 326 // To be called either by the processEvent method to set the state 327 // back to NO_CURRENT_EVENT or by the MultitaskListener at the end of 328 // a multitask phase 329 private void stopEvent() { 330 Collection interests = null; 331 VisEvent visEvent = currentEvent; 332 String viewableName = visEvent.getViewableName(); 333 stateModel. 334 setCurrentState(VisClientStateModel.EVENT_IS_FINISHED); 335 336 // Hand the event off to the scenario manager which will either record 337 // the event or replay some commands or both 338 ScenarioManager.getInstance().processEvent(visEvent); 339 340 viewerManager.stopEvent(); 341 342 if(visEvent instanceof CreateEvent) 343 { 344 interests = 345 viewerManager.getInterestSpecs(viewableName); 346 try 347 { 348 expressInterestsToEclipse(viewableName, interests); 349 } 350 catch(IOException ioe){recover_ioe(ioe);} 351 addInterestSynchronisers(interests); 352 } 353 354 stateModel. 355 setCurrentState(VisClientStateModel.NO_CURRENT_EVENT); 356 357 // Hand the event off to the scenario manager which will either record 358 // the event or replay some commands or both 359 ScenarioManager.getInstance().processEvent(visEvent); 360 361 if (DebuggingSupport.logMessages) { 362 DebuggingSupport.logMessage(this, "returning to ECLiPSe"); 363 } 364 365 currentEvent = null; 366 } 367 368 private void addInterestSynchronisers(Collection interestsList) 369 { 370 Iterator interestsListIterator = interestsList.iterator(); 371 InterestSpec interestSpec; 372 while(interestsListIterator.hasNext()) 373 { 374 interestSpec = (InterestSpec) interestsListIterator.next(); 375 interestSpec.getPropertyChangeSupport(). 376 addPropertyChangeListener("viewGranularity", 377 new InterestSpecSynchroniser(interestSpec)); 378 } 379 } 380 381 private void expressInterestsToEclipse(String viewableName, 382 Collection interestsList) 383 throws IOException 384 { 385 Atom yesNoAtom; 386 if(interestsList.isEmpty()) 387 { 388 yesNoAtom = new Atom("no"); 389 } 390 else 391 { 392 yesNoAtom = new Atom("yes"); 393 } 394 395 396 if (DebuggingSupport.logMessages) { 397 DebuggingSupport.logMessage(this, "for viewable:"+viewableName); 398 } 399 400 401 402 if (DebuggingSupport.logMessages) { 403 DebuggingSupport.logMessage(this, "writing interest:"+interestsList); 404 } 405 406 407 interest_stream_f.write(new CompoundTermImpl( 408 "interest", 409 new CompoundTermImpl("viewable_create", 410 new Atom(viewableName)), 411 yesNoAtom, 412 interestsList)); 413 414 interest_stream_f.flush(); 415 } 416 417 private void initialise() throws IOException, EclipseException, VisException 418 { 419 initialiseStreams(); 420 initialiseQueueListeners(); 421 registerVC(); 422 initialiseStateModel(); 423 initialiseViewerManager(); 424 initialiseScenarioManager(); 425 } 426 427 private void initialiseStateModel() throws EclipseException, IOException 428 { 429 stateModel = new VisClientStateModel(eclipse, clientName, this); 430 431 stateModel.setEclipseLibDir(eclipse.rpc("get_flag(installation_directory, _)").arg(2)+ 432 File.separator+"lib"+File.separator+ 433 eclipse.rpc("get_flag(hostarch,_)").arg(2)); 434 } 435 436 private void initialiseViewerManager() 437 { 438 viewerManager = new ViewerManager(stateModel); 439 } 440 441 private void initialiseScenarioManager() 442 { 443 ScenarioManager.initialise(stateModel); 444 } 445 446 private void initialiseQueueListeners() throws IOException 447 { 448 viewables_stream.setListener(new ViewablesQL()); 449 updates_stream.setListener(new UpdatesQL()); 450 } 451 452 // note slight complication: we ensure a unique visClient name by suffixing 453 // a number. 454 private void registerVC() throws IOException, EclipseException, VisException 455 { 456 int n = 0; 457 clientName = null; 458 eclipse.rpc("ensure_loaded(library(vc_support))"); 459 protocol_version_supported = false; 460 while(clientName == null) 461 { 462 interest_stream_f.write(new CompoundTermImpl("vis_protocol_version", 463 new Integer(SUPPORTED_PROTOCOL_VERSION))); 464 interest_stream_f.flush(); 465 try 466 { 467 eclipse.rpc(":", vcSupportAtom, 468 new CompoundTermImpl("vis_client_register", 469 new Atom(eclipse.getPeerName().functor() + "_jvc_"+n), 470 new Atom(viewables_stream_name), 471 new Atom(updates_stream_name), 472 new Atom(interest_stream_name)) 473 ); 474 } 475 catch(Fail f) 476 // There are two fail cases: where the vis client name was not unique, and 477 // where the protocol is unsupported (detected by a boolean flag which will 478 // have been set by the viewables QL). 479 { 480 if(!protocol_version_supported) 481 { 482 throw new VisException("The visualisation protocol version is not supported."); 483 } 484 n++; 485 } 486 clientName = new Atom(eclipse.getPeerName().functor() + "_jvc_"+n); 487 } 488 } 489 490 491 492 private void initialiseStreams() throws IOException, EclipseException 493 { 494 // NOTE: a slight complication: we use findUnusedStreamName to ensure that 495 // the stream name is unique. 496 String root_viewables_name, root_interest_name, root_updates_name; 497 498 root_viewables_name = eclipse.getPeerName().functor() + "_jvc_viewables"; 499 viewables_stream_name = findUnusedStreamName(root_viewables_name); 500 viewables_stream = eclipse.getFromEclipseQueue(viewables_stream_name); 501 viewables_stream.setListener(new ViewablesQL()); 502 503 root_updates_name = eclipse.getPeerName().functor() + "_jvc_updates"; 504 updates_stream_name = findUnusedStreamName(root_updates_name); 505 updates_stream = eclipse.getFromEclipseQueue(updates_stream_name); 506 updates_stream.setListener(new UpdatesQL()); 507 508 root_interest_name = eclipse.getPeerName().functor() + "_jvc_interest"; 509 interest_stream_name = findUnusedStreamName(root_interest_name); 510 interest_stream = eclipse.getToEclipseQueue(interest_stream_name); 511 interest_stream_f = new EXDROutputStream(interest_stream); 512 513 // Register this peer as being interested in Multitasking phases 514 eclipse.registerMultitask(new VisMultitaskListener()); 515 } 516 517 private String findUnusedStreamName(String name) 518 throws IOException, EclipseException 519 { 520 String result = null; 521 int n = 0; 522 while(result == null) 523 { 524 try 525 { 526 eclipse.rpc("current_stream", new Atom(name+n)); 527 } 528 catch(Fail f) 529 { 530 return(name+n); 531 } 532 n++; 533 } 534 return(name+n); 535 } 536 537 538 /** 539 * Provides access to the ViewerManager 540 */ 541 public ViewerManager getViewerManager() { 542 return viewerManager; 543 } 544 545 /** Generic VisEvents queue listener: detaAvailable method sets up EXDR parsing 546 * input stream if needed and reads the next eventTerm, catching any 547 * IOException. */ 548 private abstract class VisEventsQL implements QueueListener 549 { 550 protected CompoundTerm eventTerm; 551 EXDRInputStream eis = null; 552 public void dataAvailable(Object source) 553 { 554 if(eis == null) 555 { 556 FromEclipseQueue feq = (FromEclipseQueue) source; 557 eis = new EXDRInputStream(feq); 558 } 559 try 560 { 561 eventTerm = (CompoundTerm) eis.readTerm(); 562 } 563 catch(IOException ioe){recover_ioe(ioe);} 564 } 565 566 567 public void dataRequest(Object source){} 568 } 569 570 /** MultitaskListener attached to the EclipseMultitaskConnection. 571 Behaviour is to set the VisClientState to indicate being in 572 multi-tasking mode */ 573 private class VisMultitaskListener implements MultitaskListener 574 { 575 public void starting(EclipseMultitaskConnection eclipseCon, 576 String type) { 577 if (DebuggingSupport.logMessages) { 578 DebuggingSupport.logMessage(this, 579 "VisClient multitask starting type="+type); 580 } 581 if(stateModel.getTerminate()) { 582 terminate(); 583 } 584 // enable the continue button 585 stateModel.setCanPerformRPC(true); 586 if (DebuggingSupport.logMessages) { 587 DebuggingSupport.logMessage(this, 588 "VisClient multitask set RPC flag"); 589 } 590 if (MULTITASK_PHASE_TYPE.equals(type)) { 591 // A multitask phase has begun 592 if (viewerManager.shouldHold() || stateModel.getInterrupt()) { 593 // execute peer_set_multitask 594 try { 595 eclipse.multitaskConfirm(); 596 } catch(IOException ioe) { 597 recover_ioe(ioe); 598 } catch(EclipseException ee) { 599 recover_ee(ee); 600 } 601 } 602 } 603 if (DebuggingSupport.logMessages) { 604 DebuggingSupport.logMessage(this, 605 "VisClient multitask started type="+type); 606 } 607 } 608 609 public void ending(EclipseMultitaskConnection eclipseCon, 610 String type) { 611 if (DebuggingSupport.logMessages) { 612 DebuggingSupport.logMessage(this, 613 "VisClient multitask ending type="+type); 614 } 615 if(stateModel.getTerminate()) { 616 terminate(); 617 } 618 stateModel.setCanPerformRPC(false); 619 if (MULTITASK_PHASE_TYPE.equals(type)) { 620 stopEvent(); 621 } 622 } 623 } 624 625 626 /** Queue listener attached to "updates" stream. Behaviour is simple: its 627 * dataAvailable method simply gets the eventTerm and processes it */ 628 private class UpdatesQL extends VisEventsQL 629 { 630 public void dataAvailable(Object source) 631 { 632 super.dataAvailable(source); 633 VisEvent event = null; 634 try 635 { 636 event = UpdateEvent.parseFromCompoundTerm(eventTerm); 637 } 638 catch(VisException ve){recover_ve(ve);} 639 try { 640 processEvent(event); 641 } catch(RuntimeException re) { 642 recover_re(re); 643 } 644 } 645 } 646 647 648 private class ViewablesQL extends VisEventsQL 649 { 650 /** In the case of the viewables QL, dataAvailable is also able to handle the 651 * termination and protocol version messages which may appear on this stream */ 652 public void dataAvailable(Object source) 653 { 654 super.dataAvailable(source); 655 if(eventTerm.equals(terminateAtom)) 656 { 657 658 if (DebuggingSupport.logMessages) { 659 DebuggingSupport.logMessage(this, "read terminate atom"); 660 } 661 662 terminate(); 663 return; 664 } 665 if(eventTerm.equals(visProtocolSupportedAtom)) 666 { 667 protocol_version_supported = true; 668 return; 669 } 670 if(eventTerm.functor().equals("vis_protocol_version") && 671 eventTerm.arity() == 1) 672 { 673 protocol_version_supported = false; 674 return; 675 } 676 parseAndProcess(eventTerm); 677 } 678 /** parseAndProcess is called on the eventTerm. As well as parsing the term, 679 * it also queries the viewable's size if the event type indicates that this 680 * information is required. */ 681 private void parseAndProcess(CompoundTerm eventTerm) 682 { 683 VisEvent visEvent = null; 684 try 685 { 686 visEvent = VisEvent.eventFromCompoundTerm(eventTerm); 687 if(visEvent instanceof CreateEvent) 688 { 689 CompoundTermImpl sizeResult = 690 sizeGoal(visEvent); 691 ((CreateEvent) visEvent).setViewableSize((List) sizeResult.argCT(2).arg(2)); 692 } 693 if(visEvent instanceof SizeEvent) 694 { 695 CompoundTermImpl sizeResult = 696 sizeGoal(visEvent); 697 ((SizeEvent) visEvent).setViewableSize((List) sizeResult.argCT(2).arg(2)); 698 } 699 } 700 catch(VisException ve){recover_ve(ve);} 701 try { 702 processEvent(visEvent); 703 } catch(RuntimeException re) { 704 recover_re(re); 705 } 706 } 707 708 private CompoundTermImpl sizeGoal(VisEvent visEvent) 709 { 710 try 711 { 712 return((CompoundTermImpl) eclipse.rpc(new CompoundTermImpl( 713 ":", 714 vcSupportAtom, 715 new CompoundTermImpl("viewable_size", 716 new Atom(visEvent.getViewableName()), 717 null)))); 718 } 719 catch(EclipseException ee){recover_ee(ee);} 720 catch(IOException ioe){recover_ioe(ioe);} 721 return(null); 722 } 723 724 } 725 726 /** synchronises the view granularity between the java rep of the 727 * interest and the ECLiPSe rep. Added as a propertyChangeListener 728 * to the interestSpec as soon as an Eclipse-side rep is 729 * established. If the viewGranularity changes on the Java side, the 730 * synchroniser uses a multitasking phase RPC to modify the ECLiPSe-side 731 * interest spec. The converse direction is not synchronised at 732 * present. Actions changing the java side rep can only be performed 733 * when the stateModel is in the multitask phase and should only be 734 * enabled during this state */ 735 private class InterestSpecSynchroniser implements PropertyChangeListener 736 { 737 private InterestSpec interestSpec; 738 InterestSpecSynchroniser(InterestSpec interestSpec) 739 { 740 this.interestSpec = interestSpec; 741 } 742 public void propertyChange(PropertyChangeEvent event) 743 { 744 CompoundTerm goal; 745 Atom visClientName = stateModel.getVisClientName(); 746 Atom viewableName = interestSpec.getViewable().getNameAtom(); 747 String interestSpecName = interestSpec.getName(); 748 Atom newViewGranularity = (Atom) event.getNewValue(); 749 goal = new CompoundTermImpl(":", vcSupportAtom, 750 new CompoundTermImpl("vis_client_interest_modify", 751 viewableName, visClientName, 752 new Atom(interestSpecName), 753 viewGranularityAtom, 754 newViewGranularity)); 755 try { 756 stateModel.executeMultitaskGoal(goal); 757 } catch(IOException ioe){ 758 recover_ioe(ioe); 759 } catch(EclipseException ee){ 760 recover_ee(ee); 761 } 762 } 763 764 } 765 766 /** Generic recovery method for IOExceptions, including EclipseTerminated. */ 767 protected void recover_ioe(IOException ioe) 768 { 769 if (DebuggingSupport.logMessages) { 770 ioe.printStackTrace(System.err); 771 } 772 if(ioe instanceof EclipseTerminatedException) 773 { 774 viewerManager.errorDialog("The ECLiPSe process which the visualisation"+ 775 "\nclient was connected to has terminated."+ 776 "\n\nThe visualisation client will now exit."); 777 exitError(); 778 } else if(stateModel.getTerminate() && ioe instanceof SocketException) { 779 // do not print any message as this is to be expected during 780 // the termination phase 781 exitNormal(); 782 } 783 else 784 { 785 viewerManager.errorDialog("The following IOException was raised: \n\n"+ioe+ 786 "\n\nThe visualisation client will now exit."); 787 exitError(); 788 } 789 } 790 791 /** Generic recovery method for EclipseException. */ 792 protected void recover_ee(EclipseException ee) 793 { 794 if (DebuggingSupport.logMessages) { 795 ee.printStackTrace(System.err); 796 } 797 viewerManager.errorDialog("The following EclipseException was raised: \n\n"+ee+ 798 "\n\nThe visualisation client will now exit."); 799 exitError(); 800 } 801 802 /** Generic recovery method for VisException. */ 803 protected void recover_ve(VisException ve) 804 { 805 if (DebuggingSupport.logMessages) { 806 ve.printStackTrace(System.err); 807 } 808 viewerManager.errorDialog("The following VisException was raised: \n\n"+ve+ 809 "\n\nThe visualisation client will now exit."); 810 exitError(); 811 } 812 813 /** Generic recovery method for RuntimeException. */ 814 protected void recover_re(RuntimeException re) 815 { 816 if (DebuggingSupport.logMessages) { 817 re.printStackTrace(System.err); 818 } 819 viewerManager.errorDialog("The following RuntimeException was raised: \n\n"+re+ 820 "\n\nThe visualisation client will now exit."); 821 exitError(); 822 } 823 824 /** Exit due to occurrence of error. The VisClient implementation does 825 * nothing: subclasses override this behaviour according to the deployment 826 * scenario */ 827 protected void exitError() 828 { 829 830 if (DebuggingSupport.logMessages) { 831 DebuggingSupport.logMessage(this, "exit error"); 832 } 833 if (stateModel.getCanPerformRPC()) { 834 endMultitaskPhase(); 835 } 836 } 837 838 /** Exit due to a terminate request. The VisClient implementation does 839 * nothing: subclasses override this behaviour according to the deployment 840 * scenario */ 841 protected void exitNormal() 842 { 843 844 if (DebuggingSupport.logMessages) { 845 DebuggingSupport.logMessage(this, "exit normal"); 846 } 847 if (stateModel.getCanPerformRPC()) { 848 endMultitaskPhase(); 849 } 850 851 } 852 853 /** 854 * Termination intiated from the Java side (although may also follow a 855 * terminate message from the eclipse side). Tries to inform Eclipse, using 856 * a terminate message on the interest stream, then closes all streams and 857 * unregisters the VC. 858 */ 859 private void terminate() 860 { 861 try 862 { 863 interest_stream_f.write(terminateAtom); 864 interest_stream_f.flush(); 865 viewables_stream.close(); 866 updates_stream.close(); 867 interest_stream.close(); 868 eclipse.rpc(new CompoundTermImpl(":", vcSupportAtom, 869 new CompoundTermImpl("vis_client_unregister", clientName))); 870 exitNormal(); 871 } 872 catch(EclipseException ee){recover_ee(ee);} 873 catch(IOException ioe){recover_ioe(ioe);} 874 } 875 876 private class AllCommandsExecuted implements Runnable { 877 public void run() { 878 stateModel.setAllScenarioCommandsExecuted(true); 879 } 880 } 881 882 883 /** 884 * Ends the multitask phase 885 */ 886 void endMultitaskPhase() { 887 try { 888 eclipse.multitaskTerminate(); 889 } catch(IOException ioe) { 890 recover_ioe(ioe); 891 } catch(EclipseException ee) { 892 recover_ee(ee); 893 } 894 } 895} 896