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) 2000 - 2006 Cisco Systems, Inc. All Rights Reserved. 18// 19// Contributor(s): Stefano Novello / Josh Singer, Parc Technologies 20// 21// END LICENSE BLOCK 22 23//Title: Java/ECLiPSe interface 24//Version: $Id: EmbeddedEclipse.java,v 1.2 2012/01/09 21:30:55 jschimpf Exp $ 25//Author: Josh Singer / Stefano Novello 26//Company: Parc Technologies 27//Description: Embedded ECLiPSe engine. 28package com.parctechnologies.eclipse; 29import java.io.*; 30import java.util.*; 31 32/** 33 * An ECLiPSe engine embedded in the JVM process. Since there can only be one 34 * embedded ECLiPSe engine at any one time, there is no public constructor and you 35 * should use the {@link EmbeddedEclipse#getInstance(EclipseEngineOptions)} 36 * method to create an embedded ECLiPSe engine if none exists. 37 * The {@link EclipseEngineOptions} 38 * object should be created, configured, 39 * and then passed to this method to start the ECLiPSe with user-defined options.<p> 40 * Once started, a reference to the <i>EmbeddedEclipse</i> instance can be 41 * obtained using the {@link EmbeddedEclipse#getInstance()} method. 42 * Note that 43 * once the EmbeddedEclipse has been shut down by calling its <code>destroy()</code> method, 44 * no more embedded ECLiPSe engines 45 * can be started using {@link EmbeddedEclipse#getInstance(EclipseEngineOptions)}. 46 * 47 * Here is some example code for working with an EmbeddedEclipse: 48 * <p><hr><blockquote><pre> 49 * // create a new EclipseEngineOptions object 50 * EclipseEngineOptions options = new EclipseEngineOptions(new File("C:\Eclipse\")); 51 * // alter the useQueues option 52 * options.setUseQueues(true); 53 * // start up the embedded Eclipse. testEclipse is a reference to the 54 * // Eclipse engine. 55 * EclipseEngine testEclipse = EmbeddedEclipse.getInstance(options); 56 * // Direct the Eclipse to load a source file. 57 * testEclipse.compile(new File(".\myEclipseSourceFile.pl")); 58 * // Execute a top-level goal in ECLiPSe. 59 * testEclipse.rpc("go"); 60 * // Destroy the Eclipse 61 * ((EmbeddedEclipse) testEclipse).destroy(); 62 * </pre></blockquote><hr> 63 * 64 * 65 * @see EclipseConnection 66 * @see EclipseEngine 67 * @see EclipseEngineOptions 68 * 69 */ 70public class EmbeddedEclipse extends EclipseConnectionImpl 71 implements EclipseConnection, EclipseEngine 72{ 73 // Class Overview 74 // 75 // Roughly speaking, this class uses the Adapter pattern to adapt the 76 // NativeEclipse class (the Java class which provides access to the C Eclipse 77 // embedding code) to the EclipseEngine interface. The abstract methods inherited 78 // from EclipseEngineImpl and EclipseConnectionImpl are implemented using calls 79 // to NativeEclipse. It also uses the Singleton pattern: there 80 // can only be one EmbeddedEclipse per JVM. 81 82 /** 83 * The singleton instance of EmbeddedEclipse. 84 */ 85 private static EmbeddedEclipse single; 86 87 // queue used to send requests to create/close queues. 88 private FromEclipseQueue embed_info; 89 // used to read exdr from the above; 90 private EXDRInputStream embed_infoEXDRInput; 91 92 /** 93 * If the embedded ECLiPSe has not yet been started in this JVM, start the 94 * embedded ECLiPSe using the options specified in the parameter 95 * <i>EclipseEngineOptions</i>. 96 * 97 * @param options user-definable options used to configure the embedded 98 * ECLiPSe engine. 99 * 100 * @throws EclipseException if the embedded ECLiPSe has already been started, 101 * or if there was some ECLiPSe-related problem whilst 102 * attempting to start ECLiPSe, for example if the ECLiPSe installation 103 * directory was not set in the supplied EclipseEngineOptions object. 104 * @throws EclipseTerminatedException if the embedded ECLiPSe has already been terminated 105 * 106 * @throws IOException if there is some other I/O-related problem starting the ECLiPSe engine. 107 * 108 */ 109 public synchronized static EmbeddedEclipse getInstance(EclipseEngineOptions options) 110 throws EclipseException, IOException 111 { 112 // if an instance has already been created 113 if(single != null) 114 { 115 // if it has been destroyed throw an exception 116 single.testTerminated(); 117 // otherwise throw an EclipseException 118 throw new EclipseException("The embedded ECLiPSe has already been started" 119 + " in this JVM."); 120 } 121 else // if no instance has been created 122 { 123 // create a new one using the param options 124 single = new EmbeddedEclipse(options); 125 // return it 126 return single; 127 } 128 } 129 130 /** 131 * Returns the unique <i>EmbeddedEclipse</i> object if the embedded ECLiPSe 132 * has been started, but not yet terminated in this JVM. 133 * @throws EclipseException if ECLiPSe has not been started. 134 * @throws EclipseTerminatedException if the embedded ECLiPSe has been 135 * terminated. 136 */ 137 public synchronized static EmbeddedEclipse getInstance() 138 throws EclipseException, IOException 139 { 140 // if an instance has already been created 141 if(single != null) 142 { 143 // if it has been destroyed throw an exception 144 single.testTerminated(); 145 // otherwise return it 146 return single; 147 } 148 else // if no instance has been created 149 { 150 throw new EclipseException("The embedded ECLiPSe has not yet been started"+ 151 " in this JVM."); 152 } 153 } 154 155 156 /** 157 * Constructs an EmbeddedEclipse using some EclipseEngineOptions 158 */ 159 private EmbeddedEclipse(EclipseEngineOptions options) throws EclipseException, 160 IOException 161 { 162 // Process the set of options. This examines the options and 163 // sets up the NativeEclipse accordingly. 164 processOptions(options); 165 166 // Initialise the NativeEclipse. Result 0 indicates successful 167 // initialisation 168 int res = NativeEclipse.init(); 169 170 if (res != 0) 171 throw new EclipseException("Eclipse init = "+res); 172 173 // Initialise toEclipse based on the pre-existing stream called 174 // "ec_rpc_in" 175 ToEclipseQueue toEclipseTEQ = createToEclipseQueue("ec_rpc_in"); 176 // underlying queue is a system queue (should not be closed when 177 // Eclipse's closeAllQueues method is called). 178 toEclipseTEQ.setSystemQueue(true); 179 180 toEclipse = new EXDROutputStream(toEclipseTEQ); 181 182 // Initialise fromEclipse based on the pre-existing stream called 183 // "ec_rpc_out" 184 FromEclipseQueue fromEclipseFEQ = createFromEclipseQueue("ec_rpc_out"); 185 // underlying queue is a system queue (should not be closed when 186 // Eclipse's closeAllQueues method is called). 187 fromEclipseFEQ.setSystemQueue(true); 188 189 fromEclipse = new EXDRInputStream(fromEclipseFEQ); 190 191 useQueues = options.isUsingQueues(); 192 193 // If options object dictates that we should use queues to represent 194 // the Eclipse's standard streams 195 if (options.isUsingQueues()) 196 { 197 // Initialise them to do so (defined in EclipseEngineImpl). 198 initialiseStandardStreamQueues(); 199 } 200 201 // do an rpc to set up the peer name from the options. 202 rpc(new CompoundTermImpl(":",new Atom("sepia_kernel"), 203 new CompoundTermImpl("set_embed_peer", 204 new Atom(options.getPeerName()), 205 new Atom("java")))); 206 207 208 // setup embed_info to be a fromEclipse queue and a systemQueue 209 embed_info = this.getFromEclipseQueue("embed_info"); 210 embed_info.setSystemQueue(true); 211 embed_infoEXDRInput = new EXDRInputStream(embed_info); 212 } 213 214 /** 215 * Process the set of options. This examines the options and 216 * sets up the NativeEclipse accordingly. 217 */ 218 private void processOptions(EclipseEngineOptions options) throws EclipseException 219 { 220 int res; // result of Native operation 221 222 // set the peer name in this object from the peer name in the options object 223 this.setPeerName(new Atom(options.getPeerName())); 224 225 // If the user has not set the Eclipse directory, throw an appropriate 226 // exception 227 if(options.getEclipseDir() == null) 228 { 229 throw new EclipseException("The location of the ECLiPSe installation was not set in the EclipseEngineOptions object supplied."); 230 } 231 232 // load the Eclipse shared libraries, based on the location of the Eclipse 233 // installation. 234 loadEclipse(options.getEclipseDir()); 235 236 // set up the installation directory in NativeEclipse according to options 237 res = NativeEclipse.setOption(StringOption.ECLIPSEDIR , options.getEclipseDir()); 238 if (res != 0) 239 throw new EclipseException( 240 "Setting eclipse installation directory to '" + 241 options.getEclipseDir() + 242 "' result = " + res); 243 244 // if user has set it in options, set up the command line options in 245 // NativeEclipse according to options 246 if(options.getCommandLineOptions() != null) 247 { 248 res = NativeEclipse.setOption(IntOption.OPTION_ARGC, options.getCommandLineOptions().length); 249 if (res != 0) 250 { 251 throw new EclipseException( 252 "Setting eclipse no. of command line arguments to '"+ 253 options.getCommandLineOptions().length + 254 "' result = "+res); 255 } 256 res = NativeEclipse.setOption(StringArrayOption.OPTION_ARGV, options.getCommandLineOptions()); 257 if (res != 0) 258 { 259 throw new EclipseException( 260 "Setting eclipse command line arguments to '"+ 261 options.getCommandLineOptions()+ 262 "' result = "+res); 263 } 264 265 266 } 267 268 // if user has set it in options, set up the default module in 269 // NativeEclipse according to options 270 if ( options.getDefaultModule() != null ) 271 { 272 res = NativeEclipse.setOption(StringOption.DEFAULT_MODULE , options.getDefaultModule()); 273 if (res != 0) 274 throw new EclipseException( 275 "Setting eclipse default module to '"+ options.getDefaultModule() + 276 "' result = "+res); 277 } 278 279 // if user has set it in options, set up the local stack size in 280 // NativeEclipse according to options 281 if ( options.getLocalSize() != 0 ) 282 { 283 res = NativeEclipse.setOption(IntOption.LOCALSIZE , options.getLocalSize() * options.MEGABYTE); 284 if (res != 0) 285 throw new EclipseException( 286 "Setting eclipse local stack size to "+ options.getLocalSize() + 287 "M result = "+res); 288 } 289 290 // if user has set it in options, set up the global stack size in 291 // NativeEclipse according to options 292 if ( options.getGlobalSize() != 0 ) 293 { 294 res = NativeEclipse.setOption(IntOption.GLOBALSIZE , options.getGlobalSize() * options.MEGABYTE); 295 if (res != 0) 296 throw new EclipseException( 297 "Setting eclipse global stack size to "+ options.getGlobalSize() + "M result = "+res); 298 } 299 300 // tell NativeEclipse whether or not we are using queues for standard streams 301 NativeEclipse.setOption(IntOption.IO , options.isUsingQueues()? 2 : 0); 302 303 // make NativeEclipse use its own cwd rather than the process's cwd 304 // in order to be more consistent with the out-of-process situation 305 // [not yet enabled for backward compatibility] 306 // NativeEclipse.setOption(IntOption.CWD_SEPARATE , 1); 307 } 308 309 310 /** 311 * load the Eclipse shared libraries, based on the location of the Eclipse 312 * installation. Throws an exception if the platform is not supported. 313 */ 314 private static void loadEclipse(String eclipseDir) 315 { 316 // What to do here varies according to platform 317 if(!Platform.getInstance().supportsEmbeddedEclipse()) 318 { 319 throw 320 new UnsupportedOperationException("The creation of an EmbeddedEclipse"+ 321 " is not supported on platform "+ System.getProperty("os.name")+ 322 "/"+ System.getProperty("os.arch") + "."); 323 } 324 else 325 { 326 Platform.getInstance().loadEclipseSharedLibrary(new File(eclipseDir)); 327 } 328 } 329 330 /** 331 * Codes to indicate what happened while eclipse ran. 332 */ 333 private interface resume_result 334 { 335 static final int PSUCCEED = 0; // success 336 static final int PFAIL = 1; // failure 337 static final int PTHROW = 2; // exit_block, ball in A1 338 static final int PYIELD = 4; // Eclipse engine suspended 339 static final int PRUNNING = 5; // Eclipse engine running 340 static final int PWAITIO = 6; // waiting for queue input 341 static final int PFLUSHIO = 7; // request queue output 342 } 343 344 /** 345 * The String typed options 346 */ 347 private interface StringOption 348 { 349 static final int DEFAULT_MODULE = 10; 350 static final int ECLIPSEDIR = 11; 351 } 352 353 /** 354 * The StringArray typed options 355 */ 356 private interface StringArrayOption 357 { 358 static final int OPTION_ARGV = 3; 359 } 360 361 /** 362 * The int typed options 363 */ 364 private interface IntOption 365 { 366 static final int LOCALSIZE = 4; 367 static final int GLOBALSIZE = 5; 368 static final int PRIVATESIZE = 6; 369 static final int SHAREDSIZE = 7; 370 static final int IO = 12; 371 static final int OPTION_ARGC = 2; 372 static final int CWD_SEPARATE = 15; 373 } 374 375 /** 376 * Read <code>len</code> bytes from this ECLiPSe's input queue stream number 377 * <code>streamid</code> at offset <code>off</code> and store them in 378 * byte array <code>b</code>. 379 * 380 * @returns the number of bytes read. 381 */ 382 synchronized int readFromStream(int streamid, int off, int len, byte[] b) 383 throws IOException 384 { 385 int res = NativeEclipse.QueueRead(streamid, off, len, b); 386 // if res is negative, this is an error code, so throw an exception. 387 if(res < 0) 388 { 389 throw new IOException("NativeEclipse.QueueRead exited with error code "+res); 390 } 391 return(res); 392 } 393 394 /** 395 * Read a single byte from this ECLiPSe's stream number 396 * <code>streamid</code> 397 * 398 * @returns byte read, an int between 0 and 255 or -1 if zero bytes were read. 399 */ 400 synchronized int readByteFromStream(int streamid) throws IOException 401 { 402 int res = NativeEclipse.QueueReadByte(streamid); 403 if(res == -1) 404 { 405 return(-1); 406 } 407 if(res < -1) 408 { 409 throw new IOException("NativeEclipse.QueueReadByte exited with error code "+res); 410 } 411 return(res); 412 } 413 414 415 /** 416 * Returns the number of bytes available on stream streamid which may be 417 * read or skipped over without blocking. 418 */ 419 synchronized int availableOnStream(int streamid) throws IOException 420 { 421 int res = NativeEclipse.QueueAvailable(streamid); 422 // if res is negative, this is an error code, so throw an exception. 423 if(res < 0) 424 { 425 throw new IOException("NativeEclipse.QueueAvailable exited with error code "+res); 426 } 427 return(res); 428 } 429 430 /** 431 * Write <code>len</code> bytes to this ECLiPSe's output queue stream number 432 * <code>streamid</code> at offset <code>off</code> from 433 * byte array <code>b</code>. 434 * 435 * @returns the number of bytes written. 436 */ 437 synchronized int writeToStream(int streamid, byte[] b, int off, int len) 438 throws IOException 439 { 440 int res = NativeEclipse.QueueWrite(streamid, b, off, len); 441 // if res is negative, this is an error code, so throw an exception. 442 if(res < 0) 443 { 444 throw new IOException("NativeEclipse.QueueWrite exited with error code "+res); 445 } 446 return(res); 447 } 448 449 /** 450 * Write a single byte to this ECLiPSe's stream number 451 * <code>streamid</code>. 452 * 453 */ 454 synchronized void writeByteToStream(int streamid, byte b) 455 throws IOException 456 { 457 int res = NativeEclipse.QueueWriteByte(streamid, b); 458 // if res is negative, this is an error code, so throw an exception. 459 if(res < 0) 460 { 461 throw new IOException("NativeEclipse.QueueWriteByte exited with error code "+res); 462 } 463 return; 464 } 465 466 467 ControlSignal getNextControlSignal(boolean isFirstIteration, 468 boolean transferControlWithResume) 469 throws IOException 470 { 471 int res; 472 // in the cases where the resulting event code is a queue operation, 473 // during HandleEvents, stream gets set by Eclipse to the number of the 474 // relavent stream 475 Integer stream = new Integer(0); 476 477 if(isFirstIteration) 478 { 479 // resume eclipse to handle events only (and not execute posted goals) 480 // and set res to the resulting event code 481 res = NativeEclipse.HandleEvents(stream); 482 } 483 else 484 { 485 // resume eclipse again, this time executing posted goals, as well as 486 // handling events. 487 res = NativeEclipse.resumeLong(stream); 488 } 489 490 if(res == resume_result.PWAITIO) 491 { 492 return(new WaitIOSignal(stream)); 493 } 494 if(res == resume_result.PFLUSHIO && 495 lookupFromEclipseQueue(stream.intValue()) == embed_info) 496 { 497 CompoundTerm nextTerm = 498 (CompoundTerm) embed_infoEXDRInput.readTerm(); 499 if(nextTerm.functor().equals("queue_connect") && 500 nextTerm.arity() == 3 && 501 nextTerm.arg(1) instanceof Atom && 502 nextTerm.arg(2) instanceof Integer && 503 nextTerm.arg(3) instanceof Atom) 504 { 505 Atom nameAtom = (Atom) nextTerm.arg(1); 506 stream = (Integer) nextTerm.arg(2); 507 Atom directionAtom = (Atom) nextTerm.arg(3); 508 return(new OpenQueueSignal(nameAtom, stream, directionAtom)); 509 } 510 if(nextTerm.functor().equals("queue_close") && 511 nextTerm.arity() == 1 && 512 nextTerm.arg(1) instanceof Integer) 513 { 514 stream = (Integer) nextTerm.arg(1); 515 return(new CloseQueueSignal(stream)); 516 } 517 } 518 if(res == resume_result.PFLUSHIO && 519 lookupFromEclipseQueue(stream.intValue()) != embed_info) 520 { 521 return(new FlushIOSignal(stream)); 522 } 523 if(res == resume_result.PSUCCEED) 524 { 525 return(new YieldSignal()); 526 } 527 if(res == resume_result.PRUNNING) 528 { 529 return(new RunningSignal()); 530 } 531 return(null); // Default: null signifies no signal was recognised 532 } 533 534 535 /** 536 * This method is invoked as part of the routine to establish a FromEclipseQueue 537 * between Eclipse and Java. It sets up the Eclipse side of the queue. 538 */ 539 void setupFromEclipseQueue(String name) 540 throws IOException, EclipseException 541 { 542 rpc(":", 543 new Atom("sepia_kernel"), 544 new CompoundTermImpl("ecl_create_embed_queue", new Atom(name), 545 new Atom("fromec"), new Atom("")) 546 ); 547 } 548 549 550 /** 551 * This method is invoked as part of the routine to establish a ToEclipseQueue 552 * between Eclipse and Java. It sets up the Eclipse side of the queue. 553 */ 554 void setupToEclipseQueue(String name) 555 throws IOException, EclipseException 556 { 557 rpc(":", 558 new Atom("sepia_kernel"), 559 new CompoundTermImpl("ecl_create_embed_queue", new Atom(name), 560 new Atom("toec"), new Atom("")) 561 ); 562 } 563 564 // Implementation of public method from EclipseConnection interface 565 public synchronized AsyncEclipseQueue getAsyncEclipseQueue(String name) 566 throws EclipseException, IOException 567 { 568 throw new IOException("Asynchronous queues not implemented for this connection type"); 569 } 570 571 void setupAsyncEclipseQueue(String name) 572 throws EclipseException, IOException 573 { 574 throw new IOException("Asynchronous queues not implemented for this connection type"); 575 } 576 577 // overrides common implementation in EclipseConnectionImpl: using class 578 // NativeEclipse is more efficient, and also necessary since we need to 579 // get a stream number in order to set up toEclipse (the rpc queue). 580 // We can't use rpc until this is done. The common implementation uses rpc 581 // so it wouldn't work. 582 583 // returns negative if there is no open stream with name streamName 584 int getStreamNumber(String streamName) 585 { 586 return(NativeEclipse.StreamNumber(streamName)); 587 } 588 589 590 // implements flushStream(id) by doing nothing (memory queues do not need to 591 // be flushed. 592 synchronized void flushStream(int id) 593 { 594 595 } 596 597 /** 598 * Send an RPC goal to ECLiPSe. 599 */ 600 void sendGoal(Object goal) throws IOException 601 { 602 toEclipse.write(goal); 603 toEclipse.flush(); 604 } 605 606 /** 607 * Receive an RPC goal from ECLiPSe. 608 */ 609 Object receiveGoal() throws IOException 610 { 611 return(fromEclipse.readTerm()); 612 } 613 614 /** 615 * Finalizer method called when object is to be garbage collected 616 */ 617 protected void finalize() throws IOException 618 { 619 this.destroy(); 620 } 621 622 void closeFromEclipseStreamEclipseSide(int streamid) throws IOException 623 { 624 super.closeFromEclipseStreamEclipseSide(streamid); 625 try 626 { 627 rpc(":", new Atom("sepia_kernel"), 628 new CompoundTermImpl("close_embed_queue_eclipseside", 629 this.getPeerName(), 630 new Integer(streamid)) 631 ); 632 } 633 catch(EclipseException e) 634 { 635 throw new IOException("Problem closing ECLiPSe stream "+streamid+ 636 ": "+e.toString()); 637 } 638 } 639 640 void closeToEclipseStreamEclipseSide(int streamid) throws IOException 641 { 642 super.closeToEclipseStreamEclipseSide(streamid); 643 try 644 { 645 rpc(":", new Atom("sepia_kernel"), 646 new CompoundTermImpl("close_embed_queue_eclipseside", 647 this.getPeerName(), 648 new Integer(streamid)) 649 ); 650 } 651 catch(EclipseException e) 652 { 653 throw new IOException("Problem closing ECLiPSe stream "+streamid+ 654 ": "+e.toString()); 655 } 656 } 657 658 659 void respondRunning() throws IOException 660 { 661 throw new IOException("NativeEclipse.HandleEvents reports that"+ 662 " an asynchronous ECLiPSe thread is already running: cannot handle"+ 663 " events in this case."); 664 } 665 666 667 void respondWaitIO(Integer streamID) throws IOException 668 { 669 // look up the queue in the toEclipseQueue register 670 ToEclipseQueue teq = lookupToEclipseQueue(streamID.intValue()); 671 // if it is not there, print a message to stderr 672 if (teq == null) { 673 System.err.println("ECLiPSe yielded after flushing stream "+streamID.intValue() + 674 " which is not registered as a ToEclipseQueue."); 675 } else { 676 // otherwise notify the queue's listener of a request for data 677 // (causes its dataRequest method to be invoked) 678 teq.notifyRequest(); 679 } 680 } 681 682 void respondFlushIO(Integer streamID) throws IOException 683 { 684 // look up the queue in the fromEclipseQueue register 685 FromEclipseQueue feq = lookupFromEclipseQueue(streamID.intValue()); 686 // if it is not there, print a message to stderr 687 if (feq == null) { 688 System.err.println("ECLiPSe yielded after reading empty stream "+streamID.intValue() + 689 " which is not registered as a FromEclipseQueue."); 690 } else { 691 // otherwise notify the queue's listener of the flushed data 692 // (causes its dataAvailable method to be invoked) 693 feq.notifyAvailable(); 694 } 695 } 696 697 698 class RunningSignal extends ControlSignal 699 { 700 void respond() throws IOException 701 { 702 respondRunning(); 703 } 704 } 705 706 707 class CloseQueueSignal extends ControlSignal 708 { 709 private Integer streamID; 710 CloseQueueSignal(Integer streamID) 711 { 712 this.streamID = streamID; 713 } 714 void respond() throws IOException 715 { 716 respondCloseQueue(streamID); 717 } 718 } 719 720 class FlushIOSignal extends ControlSignal 721 { 722 private Integer streamID; 723 FlushIOSignal(Integer streamID) 724 { 725 this.streamID = streamID; 726 } 727 void respond() throws IOException 728 { 729 respondFlushIO(streamID); 730 } 731 } 732 733 /** 734 * Queue representing the EclipseEngine's stdin stream. 735 */ 736 ToEclipseQueue stdin; 737 738 /** 739 * Queue representing the EclipseEngine's stdout stream. 740 */ 741 FromEclipseQueue stdout; 742 743 /** 744 * Queue representing the EclipseEngine's stderr stream. 745 */ 746 FromEclipseQueue stderr; 747 748 /** 749 * Terminate access to the <i>EmbeddedEclipse</i>. This closes the embedded 750 * ECLiPSe engine and frees all system resources associated with it. After 751 * <code>destroy()</code> has been invoked, use of this EmbeddedEclipse's 752 * methods will throw <i>EclipseTerminatedException</i>s. Also, once <code> 753 * destroy()</code> has been invoked, no more <i>EmbeddedEclipse</i> instances 754 * can be created during the lifetime of this Java virtual machine. 755 * 756 * @throws EclipseTerminatedException if the ECLiPSe engine has already been 757 * terminated. 758 * @throws IOException if there was a problem communicating with ECLiPSe 759 * during termination. 760 */ 761 public synchronized void destroy() throws IOException 762 { 763 testTerminated(); 764 // close all user queues, including the eclipse sides 765 this.closeAllQueues(true); 766 this.terminated = true; 767 768 NativeEclipse.Cleanup(); 769 770 } 771 772 // Implementation of public method from EclipseEngine 773 public synchronized ToEclipseQueue getEclipseStdin() throws EclipseTerminatedException 774 { 775 testTerminated(); 776 return(stdin); 777 } 778 779 // Implementation of public method from EclipseEngine 780 public synchronized FromEclipseQueue getEclipseStdout() throws EclipseTerminatedException 781 { 782 testTerminated(); 783 return(stdout); 784 } 785 786 // Implementation of public method from EclipseEngine 787 public synchronized FromEclipseQueue getEclipseStderr() throws EclipseTerminatedException 788 { 789 testTerminated(); 790 return(stderr); 791 } 792 793 // Initialise the standard streams. Assumes that the Eclipse side of these 794 // queues has already been set up. Should be called by concrete subclasses 795 // at the appropriate point during object initialisation. All these queues 796 // are system queues (they should not be closed on the Eclipse side when 797 // the Eclipse's closeAllQueues method is called). 798 void initialiseStandardStreamQueues() throws EclipseException, IOException 799 { 800 stdin = createToEclipseQueue("input"); 801 stdin.setSystemQueue(true); 802 stdout = createFromEclipseQueue("output"); 803 stdout.setSystemQueue(true); 804 stderr = createFromEclipseQueue("error"); 805 stderr.setSystemQueue(true); 806 } 807 808 private boolean useQueues; 809 810 public synchronized boolean isUsingQueues() 811 { 812 return useQueues; 813 } 814 815} 816