8.6 Asynchronous Communicating between Java and ECLiPSe
Starting with release 5.9, the OutOfProcessEclipse
and RemoteEclipse variants of the Java-ECLiPSe Interface also support
asynchronous queues through the class AsyncEclipseQueue.
These are essentially socket connections between ECLiPSe and Java, which
can be used independently of which side has control.
An AsyncEclipseQueue queue is opened and closed in the same way as
a FromEclipseQueue or ToEclipseQueue, but the following
differences exist:
-
an asynchronous queue can be read/written from the Java side
even while the ECLiPSe side has control, e.g. during an RPC. This
obviously has to happen from a different thread than the one that
executes the RPC.
- I/O operations on asynchronous queues can block, they should
therefore be done from a dedicated thread.
- the AsyncEclipseQueue class does not extend InputStream or
OutputStream and can therefore not be used for I/O directly. Instead,
a standard Java InputStream can be obtained from it via the
getInputStream() method, and an OutputStream via the getOutputStream()
method.
- on the ECLiPSe side, an event can (and should) be raised when
data arrives from the Java side. If the ECLiPSe side has control at
that time, it will handle the event. Otherwise, the event handling
may be deferred until ECLiPSe has control back.
8.6.1 Opening and closing asynchronous queues
Opening and closing can be performed in a single step from
either the Java or the ECLiPSe side.
Opening an asychronous queue using Java methods
The AsyncEclipseQueue does not have public
constructors. Instead, we invoke getAsyncEclipseQueue.
This asks the EclipseConnection object for a
reference to an AsyncEclipseQueue instance
which represents a new queue. To specify the stream for later
reference, we supply the method with a string which will equal to the
atom by which the queue is referred to as a stream in ECLiPSe. For
example the following code creates such a queue:
...
AsyncEclipseQueue java_eclipse_async =
eclipse.getAsyncEclipseQueue("java_eclipse_async");
...
Note that this method will only work when the eclipse object is
an OutOfProcessEclipse or RemoteEclipse.
Teh method will create and return a new AsyncEclipseQueue
object, and will also open a stream with the
specified names on the ECLiPSe side. No stream in ECLiPSe should
exist with the specified name. If a stream exists which has this name
and is not a queue between the Java object and ECLiPSe, the Java
method throws an exception. If the name is used by a pre-existing
queue, it is returned, so the getAsyncEclipseQueue
methods can also be used to retrieve the queue
objects by name once they have already been created.
Opening an asychronous queue using ECLiPSe predicates
You can use the ECLiPSe builtin peer_queue_create/5 to open a
queue. Used correctly, these have the same effect as the Java methods
explained above. For the peer name, you should use the atom returned
by the getPeerName() method of the relevant EclipseConnection instance. The direction should be bidirect,
and the queue type should be specified as async.
Transferring data using Java methods
Once an AsyncEclipseQueue has been created, a standard Java
InputStream can be obtained from it via the getInputStream() method,
and an OutputStream via the getOutputStream() method.
These Java streams can be used as you would any instance of java.io.InputStream or java.io.OutputStream.
Unlike the synchronous FromEclipseQueue and ToEclipseQueue,
I/O on these streams can block, and should therefore be handled by dedicated
threads. For this reason, the listener-feature is not supported on
asynchronous queues.
Transferring data using ECLiPSe predicates
On the ECLiPSe side, there are built-in predicates for writing to,
reading from and otherwise interacting with streams. Any of these may
be used. Perhaps most useful are read_exdr/2 and write_exdr/2; these are explained in Section
8.5.2. For the stream ID, you may either use the stream name, or the stream number, obtained for example using peer_get_property/3.
Since the ECLiPSe side does not support threads, it should only write to
an asynchronous queue when there is an active thread on the Java side to
read the data. Otherwise, the write-operation may block, and control will
never be transferred back from ECLiPSe to Java.
For reading from an asynchronous queue, the ECLiPSe side should
typically set up an event handler. The handler will be invoked whenever
new data arrives on a previously empty queue.
my_async_queue_handler(Stream) :-
( select([Stream], 0, [_]) ->
read_exdr(Stream, Data),
process(Data),
my_async_queue_handler(Stream)
;
events_nodefer
).
setup_async_queue_with_handler :-
event_create(my_async_queue_handler(my_async_queue), [defers], Event),
peer_queue_create(my_async_queue, host, async, bidirect, Event).
Note that execution of the handler is only guaranteed when the ECLiPSe
side has control.
When communicating between Java and ECLiPSe using queues, you
should always invoke the flush() method of the Java OutputStream which you have written to, whether it be a ToEclipseQueue or an EXDROutputStream. Similarly, on the
ECLiPSe side, flush/1 should always be executed after
writing. Although in some cases reading of the data is possible
without a flush, flushing guarantees the transfer of data.
Closing a queue using Java methods
This is done simply by calling the close() method on the AsyncEclipseQueue instance.
Closing a queue using ECLiPSe predicates
This is done by executing the builtin peer_queue_close/1. Note
that the builtin close/1 should not be used in this situation,
as it will not terminate the Java end of the queue.
8.6.2 Writing and reading ECLiPSe terms on queues
See the corresponding section for synchronous queues,
8.5.2.