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