• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /barrelfish-2018-10-04/usr/eclipseclp/JavaInterface/src/com/parctechnologies/eclipse/
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) 2001 - 2006 Cisco Systems, Inc.  All Rights Reserved.
18//
19// Contributor(s): Josh Singer, Parc Technologies
20//
21// END LICENSE BLOCK
22
23//Title:        Java/ECLiPSe interface
24//Version:      $Id: OutOfProcessEclipse.java,v 1.5 2013/02/10 18:54:45 jschimpf Exp $
25//Author:       Josh Singer
26//Company:      Parc Technologies
27//Description:  ECLiPSe engine run on local machine, separate process
28package com.parctechnologies.eclipse;
29import java.io.*;
30import java.net.*;
31
32/**
33 * An ECLiPSe engine which runs in a child process of the Java virtual machine.
34 * An <i>OutOfProcessEclipse</i> is created using the public constructor, which
35 * takes an <i>EclipseEngineOptions</i> object. A JVM may have any number of
36 * instances of this class. Invocation of the <code>destroy()</code> method does
37 * not affect the ability to create new <i>OutOfProcessEclipse</i> instances.
38 */
39public class OutOfProcessEclipse implements EclipseConnection, EclipseEngine
40{
41  private Process eclipseProcess;
42
43  private RemoteEclipse eclipseConnection;
44
45  private BufferedReader eclipseProcessStdout;
46
47  private BufferedReader eclipseProcessStderr;
48
49  private BufferedWriter eclipseProcessStdin;
50
51  private boolean useQueues;
52
53  private ToEclipseQueue stdin;
54  private FromEclipseQueue stdout, stderr;
55
56  public synchronized boolean isUsingQueues()
57  {
58    return useQueues;
59  }
60
61  /**
62   * Create a new OutOfProcessEclipse using the supplied options.
63   *
64   * @throws IOException if the connection to the child process failed
65   * @throws EclipseException if there was a problem setting up the ECLiPSe side
66   * of the connection.
67   *
68   * @param options settings for the new ECLiPSe engine.
69   */
70  public OutOfProcessEclipse(EclipseEngineOptions options)
71    throws IOException, EclipseException
72  {
73    startLocalEclipse(options);
74
75    // Read the port number from the eclipseProcess' stdout
76    String portString = eclipseProcessStdout.readLine();
77
78    // Read the password from the eclipseProcess' stdout
79    String passwd = eclipseProcessStdout.readLine();
80
81    // make port string into an int
82    int port = Integer.parseInt(portString);
83
84    // connect
85    remoteConnect(InetAddress.getByName("localhost"), port, passwd);
86
87    // set the useQueues flag according to options
88    useQueues = options.isUsingQueues();
89
90    // System.out.println("initial attachment complete");
91
92    // connect the local Eclipse's standard streams to queues,
93    // and redirect these to the System standard streams if
94    // necessary.
95    connectStdStreams();
96
97    // if not using standard stream queues, need to add listeners to them to
98    // link up with JVM's standard streams
99    if(!options.isUsingQueues())
100    {
101      stdStreamsAddListeners();
102    }
103
104
105  }
106
107  private void connectStdStreams() throws EclipseException, IOException
108  {
109    // For each standard stream, set up a queue
110    stdout = eclipseConnection.getFromEclipseQueue("joop_stdout");
111    stderr = eclipseConnection.getFromEclipseQueue("joop_stderr");
112    stdin = eclipseConnection.getToEclipseQueue("joop_stdin");
113
114    // make sure these get flushed eagerly, so users see error messages
115    eclipseConnection.rpc("set_stream_property(joop_stdout,flush,end_of_line)");
116    eclipseConnection.rpc("set_stream_property(joop_stderr,flush,end_of_line)");
117
118    // now redirect Eclipse streams to write to / read from these queues
119    // (NOTE: setting user_XXX before stdXXX is the only way to change stdXXX!)
120    eclipseConnection.rpc("set_stream(user_input, joop_stdin)");
121    eclipseConnection.rpc("set_stream(stdin, joop_stdin)");
122    eclipseConnection.rpc("set_stream(user_output, joop_stdout)");
123    eclipseConnection.rpc("set_stream(stdout, joop_stdout)");
124    eclipseConnection.rpc("set_stream(user_error, joop_stderr)");
125    eclipseConnection.rpc("set_stream(stderr, joop_stderr)");
126    // ... and all the aliases of these basic streams as well
127    eclipseConnection.rpc("set_stream(output, joop_stdout)");
128    eclipseConnection.rpc("set_stream(warning_output, joop_stdout)");
129    eclipseConnection.rpc("set_stream(log_output, joop_stdout)");
130    eclipseConnection.rpc("set_stream(error, joop_stderr)");
131    eclipseConnection.rpc("set_stream(input, joop_stdin)");
132  }
133
134  private void stdStreamsAddListeners()
135  {
136    try
137      {stdin.setListener(new StdInListener());}
138    catch(IOException ioe) // should never be thrown as queue is not closed
139      {System.err.println("Error: setting of listener threw an IOException.");}
140    try
141      {stdout.setListener(new StdOutListener("output"));}
142    catch(IOException ioe) // should never be thrown as queue is not closed
143      {System.err.println("Error: setting of listener threw an IOException.");}
144    try
145      {stderr.setListener(new StdOutListener("error"));} // no special listener for stderr
146    catch(IOException ioe) // should never be thrown as queue is not closed
147      {System.err.println("Error: setting of listener threw an IOException.");}
148  }
149
150  private void remoteConnect(InetAddress hostAddr, int port,
151                             String passwd) throws IOException, EclipseException
152  {
153
154    //System.out.println("Trying to make connection on host "+hostString+", port "
155    //                   +port+" password "+passwd);
156
157
158
159    // try to set up the connection on the specified port
160    try
161    {
162      eclipseConnection =
163        new RemoteEclipse(hostAddr, port, passwd);
164
165
166      //System.out.println("Connection successful.");
167
168      eclipseProcessStdin.write("accept. \n");
169      eclipseProcessStdin.flush();
170
171      //System.out.println("Accept signal written and flushed.");
172
173      // signal to the eclipse to resume execution + wait until it relinquishes
174      // control
175      eclipseConnection.resume();
176    }
177    catch(IOException ioe)
178    {
179      // if unsuccessful, signal to the eclipse to shut down before re-throwing
180      // exception
181      //System.out.println("Connection unsuccessful."+ioe);
182
183      try
184      {
185        eclipseProcessStdin.write("reject. \n");
186        eclipseProcessStdin.flush();
187      }
188      catch(Exception e){}
189      //System.out.println("Reject signal written and flushed.");
190
191      throw ioe;
192    }
193
194
195  }
196
197
198  private void startLocalEclipse(EclipseEngineOptions options)
199    throws EclipseException, IOException
200  {
201    // What to do here varies according to platform
202    if(!Platform.getInstance().supportsOutOfProcessEclipse())
203    {
204      throw
205        new UnsupportedOperationException("The creation of an OutOfProcessEclipse"+
206        " is not supported on platform "+ System.getProperty("os.name")+
207        "/"+ System.getProperty("os.arch") + ".");
208    }
209
210    // The command to start eclipse : varies according to platform + options
211    String[] cmdarray = new String[100];
212
213    // index of next empty string in command array
214    int cmd = 0;
215
216    if(options.getEclipseDir() == null)
217    {
218      throw new EclipseException("The location of the ECLiPSe installation was not set in the EclipseEngineOptions object supplied.");
219    }
220    File eclipseDir = new File(options.getEclipseDir());
221    File executableSubdir =
222      Platform.getInstance().getExecutableSubdirectory(eclipseDir);
223    File librarySubdir =
224      Platform.getInstance().getLibrarySubdirectory(eclipseDir);
225
226    if (!executableSubdir.equals(librarySubdir) && executableSubdir.exists())
227    {
228	// Eclipse has been fully installed, and an 'eclipse' script exists:
229	// run the script because it sets some potentially useful environment
230	// variables (e.g. LD_LIBRARY_PATH, JRE_HOME, TCL_LIBRARY)
231	cmdarray[cmd++] = (new File(executableSubdir, "eclipse")).toString();
232    }
233    else
234    {
235	// No bin-directory, i.e. we have a non-installed Unix-Eclipse, or we
236	// are on Windows (possibly without registry entries).  We try to run
237	// eclipse.exe, supplying the installation directory via the command
238	// line.  This should work for many applications, provided things like
239	// LD_LIBRARY_PATH are taken care of by the host application.
240	cmdarray[cmd++] = (new File(librarySubdir, "eclipse.exe")).toString();
241
242	cmdarray[cmd++] = "-D";
243	cmdarray[cmd++] = eclipseDir.toString();
244    }
245
246    // if user has set it in options, add a command line option to set up the
247    // local stack size
248    if(options.getLocalSize() != 0)
249    {
250      cmdarray[cmd++] = "-l";
251      cmdarray[cmd++] = (new Integer(options.getLocalSize()).toString())+"M";
252    }
253
254    // if user has set it in options, add a command line option to set up the
255    // global stack size
256    if(options.getGlobalSize() != 0)
257    {
258      cmdarray[cmd++] = "-g";
259      cmdarray[cmd++] = (new Integer(options.getGlobalSize()).toString())+"M";
260    }
261
262    cmdarray[cmd++] = "-e";
263    // if user has set default module in options, add goal to do
264    // this, and then start the joop_boot
265    if (options.getDefaultModule() != null )
266    {
267      cmdarray[cmd++] = "set_flag(toplevel_module, "+
268        options.getDefaultModule()+"), joop_boot:jb_go";
269    }
270    else // Otherwise just add a goal to start the joop_boot
271    {
272      cmdarray[cmd++] = "joop_boot:jb_go";
273    }
274
275    // Add bootfile. This is the ECLiPSe code which will set up the remote
276    // connection etc. It is initialised using the "jb_go" predicate.
277    cmdarray[cmd++] = "-b";
278
279    cmdarray[cmd++] = (new File(new File(eclipseDir, "lib"), "joop_boot.eco")).toString();
280
281    // copy to new array which is just big enough
282    String[] cmdarray2 = new String[cmd];
283    System.arraycopy(cmdarray, 0, cmdarray2, 0, cmd);
284
285    eclipseProcess = Runtime.getRuntime().exec(cmdarray2);
286
287    eclipseProcessStdout =
288      new BufferedReader(new InputStreamReader(eclipseProcess.getInputStream()));
289
290    eclipseProcessStderr =
291      new BufferedReader(new InputStreamReader(eclipseProcess.getErrorStream()));
292
293    eclipseProcessStdin =
294      new BufferedWriter(new OutputStreamWriter(eclipseProcess.getOutputStream()));
295
296    // write the peer name specified in options on stdin
297    eclipseProcessStdin.write(options.getPeerName()+". \n");
298    eclipseProcessStdin.flush();
299  }
300
301
302  /**
303   * Finalizer method called when object is to be garbage collected
304   */
305  protected void finalize() throws IOException, EclipseException
306  {
307    this.destroy();
308  }
309
310
311  public synchronized ToEclipseQueue getEclipseStdin() throws EclipseTerminatedException
312  {
313    eclipseConnection.testTerminated();
314    if(useQueues)
315      return(stdin);
316    else
317      return(null);
318  }
319
320  public synchronized FromEclipseQueue getEclipseStdout() throws EclipseTerminatedException
321  {
322    eclipseConnection.testTerminated();
323    if(useQueues)
324      return(stdout);
325    else
326      return(null);
327  }
328
329  public synchronized FromEclipseQueue getEclipseStderr() throws EclipseTerminatedException
330  {
331    eclipseConnection.testTerminated();
332    if(useQueues)
333      return(stderr);
334    else
335      return(null);
336  }
337
338  /**
339   * Terminate the <i>OutOfProcessEclipse</i> process and the connection to it.
340   * After <code>destroy()</code> has been invoked, future invocations of public
341   * methods will throw <i>EclipseTerminatedException</i>s
342   *
343   * @throws EclipseTerminatedException if the <code>destroy()</code> method had
344   * already been called.
345   */
346  public void destroy() throws IOException
347  {
348    eclipseConnection.testTerminated();
349    try // multilateral disconnect
350    {
351      eclipseConnection.disconnect();
352    }
353    catch(Exception e)
354    { // if that doesnt work, try unilateral
355      try
356      {
357        eclipseConnection.unilateralDisconnect();
358      }
359      catch(EclipseTerminatedException ete)
360      {}
361    }
362  }
363
364  public CompoundTerm rpc(String goal) throws EclipseException, IOException
365  {
366    return(eclipseConnection.rpc(goal));
367  }
368
369  public CompoundTerm rpc(CompoundTerm goal) throws EclipseException, IOException
370  {
371    return(eclipseConnection.rpc(goal));
372  }
373
374  public FromEclipseQueue getFromEclipseQueue(String name) throws EclipseException, IOException
375  {
376    return(eclipseConnection.getFromEclipseQueue(name));
377  }
378
379  public ToEclipseQueue getToEclipseQueue(String name) throws EclipseException, IOException
380  {
381    return(eclipseConnection.getToEclipseQueue(name));
382  }
383
384  public AsyncEclipseQueue getAsyncEclipseQueue(String name) throws EclipseException, IOException
385  {
386    return(eclipseConnection.getAsyncEclipseQueue(name));
387  }
388
389  public void compile(File f) throws EclipseException, IOException
390  {
391    eclipseConnection.compile(f);
392  }
393
394  public String getPath(File f) throws EclipseException, IOException
395  {
396    return(eclipseConnection.getPath(f));
397  }
398
399  public CompoundTerm rpc(String functor, Object arg1) throws EclipseException, IOException
400  {
401    return(eclipseConnection.rpc(functor, arg1));
402  }
403
404  public CompoundTerm rpc(String functor, Object arg1,
405                          Object arg2) throws EclipseException, IOException
406  {
407    return(eclipseConnection.rpc(functor, arg1, arg2));
408  }
409
410  public CompoundTerm rpc(String functor, Object arg1,
411                          Object arg2, Object arg3) throws EclipseException, IOException
412  {
413    return(eclipseConnection.rpc(functor, arg1, arg2, arg3));
414  }
415
416  public CompoundTerm rpc(String functor, Object arg1,
417                          Object arg2, Object arg3, Object arg4)
418                          throws EclipseException, IOException
419  {
420    return(eclipseConnection.rpc(functor, arg1, arg2, arg3, arg4));
421  }
422
423  public CompoundTerm rpc(String functor, Object arg1,
424                          Object arg2, Object arg3, Object arg4,
425                          Object arg5) throws EclipseException, IOException
426  {
427    return(eclipseConnection.rpc(functor, arg1, arg2, arg3, arg4, arg5));
428  }
429
430  public CompoundTerm rpc(String functor, Object[] args) throws EclipseException, IOException
431  {
432    return(eclipseConnection.rpc(functor, args));
433  }
434
435  public CompoundTerm rpc(Object[] goalTerm) throws EclipseException, IOException
436  {
437    return(eclipseConnection.rpc(goalTerm));
438  }
439
440  public Atom getPeerName()
441  {
442    return(eclipseConnection.getPeerName());
443  }
444
445  private class StdInListener implements QueueListener
446  {
447    // size of byte chunk we attempt to read from stdin when there is a request
448    private static final int READ_CHUNK_SIZE = 512;
449
450    public void dataAvailable(Object source)
451    {
452    }
453
454    public void dataRequest(Object source)
455    {
456      ToEclipseQueue teq = (ToEclipseQueue) source;
457
458      byte[] chunk = new byte[READ_CHUNK_SIZE];
459      int readResult ;
460
461      try
462      {
463        readResult = System.in.read(chunk);
464        if(readResult == -1) // EOF
465        {
466           // do nothing?
467        }
468        else // write the part of chunk to queue
469        {
470          teq.write(chunk, 0, readResult);
471          teq.flush();
472        }
473      }
474      catch(IOException ioe)
475      {
476        // write these out to stderr
477        System.err.println("Attempt to read from JVM's stdin produced exception:\n"+ioe);
478        System.err.flush();
479      }
480    }
481  }
482
483  private class StdOutListener implements QueueListener
484  {
485    private String destination;
486
487    public StdOutListener(String destination)
488    {
489      this.destination = destination;
490    }
491    public void dataAvailable(Object source)
492    {
493      FromEclipseQueue feq = (FromEclipseQueue) source;
494      byte[] chunk;
495
496      try
497      {
498        int availableBytes = feq.available();
499        chunk = new byte[availableBytes];
500        feq.read(chunk);
501      }
502      catch(IOException ioe)
503      {
504        // write these out to stderr
505        System.err.println("Attempt to use ECLiPSe's output queue produced exception:\n"
506                            +ioe);
507        System.err.flush();
508        return;
509      }
510      if(destination.equals("output"))
511      {
512        System.out.print(new String(chunk));
513        return;
514      }
515      if(destination.equals("error"))
516      {
517        System.err.print(new String(chunk));
518        return;
519      }
520
521    }
522
523    public void dataRequest(Object source)
524    {
525    }
526  }
527
528  // implements method from EclipseConnection
529  public EclipseMultitaskConnection registerMultitask(MultitaskListener multitaskListener) throws EclipseException,IOException {
530    return eclipseConnection.registerMultitask(multitaskListener);
531  }
532}
533