ICE Manual(Documentation for Ice 3.5)---Java Mapping--Client-Side
2013-04-23 16:58
435 查看
Client-Side
Slice-to-Java Mapping
In this section, we present the client-side Slice-to-Java mapping. The client-side Slice-to-Java mapping defines how Slice data types are translated to Java types, and how clients invoke operations, pass parameters, and handle errors. Much of theJava mapping is intuitive. For example, Slice sequences map to Java arrays, so there is essentially nothing new you have to learn in order to use Slice sequences in Java.
The Java API to the Ice run time is fully thread-safe. Obviously, you must still synchronize access to data from different threads. For example, if you have two threads sharing a sequence, you cannot safely have one thread insert into the sequence
while another thread is iterating over the sequence. However, you only need to concern yourself with concurrent access to your own data — the Ice run time itself is fully thread safe, and none of the Ice API calls require you to acquire or release a lock before
you safely can make the call.
Much of what appears in this chapter is reference material. We suggest that you skim the material on the initial reading and refer back to specific sections as needed. However, we recommend that you read at least the mappings for exceptions, interfaces,
and operationsin detail because these sections cover how to call operations from a client, pass parameters,
and handle exceptions.
In order to use the Java mapping, you should need no more than the Slice definition of your application and knowledge of the Java mapping rules. In particular, looking through the generated code in order to discern how to use the Java mapping is likely to be inefficient, due to the amount of detail. Of course, occasionally, you may want to refer to the generated code to confirm a detail of the mapping, but we recommend that you otherwise use the material presented here to see how to write your client-side code. |
The IcePackage All of the APIs for the Ice run time are nested in the Icepackage, to avoid clashes with definitions for other libraries or applications. Some of the contents of the Icepackage are generated from Slice definitions; other parts of the Icepackage provide special-purpose definitions that do not have a corresponding Slice definition. We will incrementally cover the contents of the Icepackage throughout the remainder of the manual. |
Asynchronous
Method Invocation (AMI) in Java
Asynchronous Method Invocation (AMI) is the term used to describe the client-side support for the asynchronous programming model. AMI supports both oneway and twoway requests, but unlike their synchronous counterparts, AMIrequests never block the calling thread. When a client issues an AMI request, the Ice run time hands the message off to the local transport buffer or, if the buffer is currently full, queues the request for later delivery. The application can then continue
its activities and poll or wait for completion of the invocation, or receive a callback when the invocation completes.
AMI is transparent to the server: there is no way for the server to tell whether a client sent a request synchronously or asynchronously.
As of version 3.4, Ice provides a new API for asynchronous method invocation. This page describes the new API. Note that the old API is deprecated and will be removed in a future release. |
Basic Asynchronous API in Java
Consider the following simple Slice definition:Slice
module Demo { interface Employees { string getName(int number); }; };
Asynchronous Proxy Methods in Java
Besides the synchronous proxy methods, slice2javagenerates the following asynchronous proxy methods:
Java
public interface EmployeesPrx extends Ice.ObjectPrx { // ... public Ice.AsyncResult begin_getName(int number); public Ice.AsyncResult begin_getName(int number, java.util.Map<String, String> __ctx); public String end_getName(Ice.AsyncResult __result); }
Four additional overloads of begin_getNameare generated for use with generic completion callbacks and type-safe completion callbacks. |
getNameoperation results in
begin_getNameand
end_getNamemethods. (The
begin_method
is overloaded so you can pass a per-invocation context.)
The
begin_getNamemethod sends (or queues) an invocation of
getName. This
method does not block the calling thread.
The
end_getNamemethod collects the result of the asynchronous invocation. If, at the time the calling thread calls
end_getName,
the result is not yet available, the calling thread blocks until the invocation completes. Otherwise, if the invocation completed some time before the call to
end_getName, the method returns immediately with
the result.
A client could call these methods as follows:
Java
EmployeesPrx e = ...; Ice.AsyncResult r = e.begin_getName(99); // Continue to do other things here... String name = e.end_getName(r);
Because
begin_getNamedoes not block, the calling thread can do other things while the operation is in progress.
Note that
begin_getNamereturns a value of type
AsyncResult. This value contains the state that the Ice run time requires to keep track of the asynchronous invocation. You must pass
the
AsyncResultthat is returned by the
begin_method to the corresponding
end_method.
The
begin_method has one parameter for each in-parameter of the corresponding Slice operation. Similarly, the
end_method has one out-parameter for each out-parameter
of the corresponding Slice operation (plus the
AsyncResultparameter). For example, consider the following operation:
Slice
double op(int inp1, string inp2, out bool outp1, out long outp2);
The
begin_opand
end_opmethods have the following signature:
Java
Ice.AsyncResult begin_op(int inp1, String inp2); Ice.AsyncResult begin_op(int inp1, String inp2, java.util.Map<String, String> __ctx); double end_op(Ice.BooleanHolder outp1, Ice.LongHolder outp2, Ice.AsyncResult r);
Asynchronous Exception Semantics in Java
If an invocation raises an exception, the exception is thrown by the end_method, even if the actual error condition for the exception was encountered during the
begin_method
("on the way out"). The advantage of this behavior is that all exception handling is located with the code that calls the
end_method (instead of being present twice, once where the
begin_method
is called, and again where the
end_method is called).
There is one exception to the above rule: if you destroy the communicator and then make an asynchronous invocation, the
begin_method throws
CommunicatorDestroyedException. This is
necessary because, once the run time is finalized, it can no longer throw an exception from the
end_method.
The only other exception that is thrown by the
begin_and
end_methods is
java.lang.IllegalArgumentException. This exception indicates that you
have used the API incorrectly. For example, the
begin_method throws this exception if you call an operation that has a return value or out-parameters on a oneway proxy. Similarly, the
end_method
throws this exception if you use a different proxy to call the
end_method than the proxy you used to call the
begin_method, or if the
AsyncResultyou
pass to the
end_method was obtained by calling the
begin_method for a different operation.
AsyncResult
Class in Java
The AsyncResultthat is returned by the
begin_method encapsulates the state of the asynchronous invocation:
Java
public class AsyncResult { public Communicator getCommunicator(); public Connection getConnection(); public ObjectPrx getProxy(); public String getOperation(); public boolean isCompleted(); public void waitForCompleted(); public boolean isSent(); public void waitForSent(); public void throwLocalException(); public boolean sentSynchronously(); }
The methods have the following semantics:
Communicator getCommunicator()
This method returns the communicator that sent the invocation.
Connection getConnection()
This method returns the connection that was used for the invocation. Note that, for typical asynchronous proxy invocations, this method returns a nil value because the possibility of automatic retries means the connection that is currently in use could change
unexpectedly. The
getConnectionmethod only returns a non-nil value when the
AsyncResultobject is obtained by calling
begin_flushBatchRequestson a
Connectionobject.
ObjectPrx getProxy()
This method returns the proxy that was used to call the
begin_method, or nil if the
AsyncResultobject was not obtained via an asynchronous proxy invocation.
String getOperation()
This method returns the name of the operation.
boolean isCompleted()
This method returns true if, at the time it is called, the result of an invocation is available, indicating that a call to the
end_method will not block the caller. Otherwise, if the result is
not yet available, the method returns false.
void waitForCompleted()
This method blocks the caller until the result of an invocation becomes available.
boolean isSent()
When you call the
begin_method, the Ice run time attempts to write the corresponding request to the client-side transport. If the transport cannot accept the request, the Ice run time queues the request for later transmission.
isSentreturns
true if, at the time it is called, the request has been written to the local transport (whether it was initially queued or not). Otherwise, if the request is still queued or an exception occurred before the request could be sent,
isSentreturns
false.
void waitForSent()
This method blocks the calling thread until a request has been written to the client-side transport, or an exception occurs. After
waitForSentreturns,
isSentreturns true if the
request was successfully written to the client-side transport, or false if an exception occurred. In the case of a failure, you can call the corresponding
end_method or
throwLocalExceptionto
obtain the exception.
void throwLocalException()
This method throws the local exception that caused the invocation to fail. If no exception has occurred yet,
throwLocalExceptiondoes nothing.
boolean sentSynchronously()
This method returns true if a request was written to the client-side transport without first being queued. If the request was initially queued,
sentSynchronouslyreturns false (independent of whether the request is still
in the queue or has since been written to the client-side transport).
Polling for Completion in Java
The AsyncResultmethods allow you to poll for call completion. Polling is useful in a variety of cases. As an example, consider the following simple interface to transfer files from client to server:
Slice
interface FileTransfer { void send(int offset, ByteSeq bytes); };
The client repeatedly calls
sendto send a chunk of the file, indicating at which offset in the file the chunk belongs. A naïve way to transmit a file would be along the following lines:
Java
FileHandle file = open(...); FileTransferPrx ft = ...; int chunkSize = ...; int offset = 0; while (!file.eof()) { byte[] bs; bs = file.read(chunkSize); // Read a chunk ft.send(offset, bs); // Send the chunk offset += bs.length; }
This works, but not very well: because the client makes synchronous calls, it writes each chunk on the wire and then waits for the server to receive the data, process it, and return a reply before writing the next chunk. This means that both client
and server spend much of their time doing nothing — the client does nothing while the server processes the data, and the server does nothing while it waits for the client to send the next chunk.
Using asynchronous calls, we can improve on this considerably:
Java
FileHandle file = open(...); FileTransferPrx ft = ...; int chunkSize = ...; int offset = 0; LinkedList<Ice.AsyncResult> results = new LinkedList<Ice.AsyncResult>(); int numRequests = 5; while (!file.eof()) { byte[] bs; bs = file.read(chunkSize); // Send up to numRequests + 1 chunks asynchronously. Ice.AsyncResult r = ft.begin_send(offset, bs); offset += bs.length; // Wait until this request has been passed to the transport. r.waitForSent(); results.add(r); // Once there are more than numRequests, wait for the least // recent one to complete. while (results.size() > numRequests) { Ice.AsyncResult r = results.getFirst(); results.removeFirst(); r.waitForCompleted(); } } // Wait for any remaining requests to complete. while (results.size() > 0) { Ice.AsyncResult r = results.getFirst(); results.removeFirst(); r.waitForCompleted(); }
With this code, the client sends up to
numRequests + 1chunks before it waits for the least recent one of these requests to complete. In other words, the client sends the next request without waiting for the
preceding request to complete, up to the limit set by
numRequests. In effect, this allows the client to "keep the pipe to the server full of data": the client keeps sending data, so both client and server continuously do work.
Obviously, the correct chunk size and value of
numRequestsdepend on the bandwidth of the network as well as the amount of time taken by the server to process each request. However, with a little testing, you
can quickly zoom in on the point where making the requests larger or queuing more requests no longer improves performance. With this technique, you can realize the full bandwidth of the link to within a percent or two of the theoretical bandwidth limit of
a native socket connection.
Generic Completion Callbacks in Java
The begin_method is overloaded to allow you to provide completion callbacks. Here are the corresponding methods for the
getNameoperation:
Java
Ice.AsyncResult begin_getName(int number, Ice.Callback __cb); Ice.AsyncResult begin_getName(int number, java.util.Map<String, String> __ctx, Ice.Callback __cb);
The second version of
begin_getNamelets you override the default context. Following the in-parameters, the
begin_method accepts a parameter of type
Ice.Callback,
which is a callback class with a
completedmethod that you must provide. The Ice run time invokes the
completedmethod when an asynchronous operation completes. For example:
Java
public class MyCallback extends Ice.Callback { public void completed(Ice.AsyncResult r) { EmployeesPrx e = (EmployeesPrx)r.getProxy(); try { String name = e.end_getName(r); System.out.println("Name is: " + name); } catch (Ice.LocalException ex) { System.err.println("Exception is: " + ex); } } }
Note that your callback class must derive from
Ice.Callback. The implementation of your callback method must call the
end_method. The proxy for the call is available via the
getProxymethod
on the
AsyncResultthat is passed by the Ice run time. The return type of
getProxyis
Ice.ObjectPrx, so you must down-cast the proxy to its correct type.
Your callback method should catch and handle any exceptions that may be thrown by the
end_method. If an operation can throw user exceptions, this means that you need an additional catch handler for
Ice.UserException(or
catch all possible user exceptions explicitly). If you allow an exception to escape from the callback method, the Ice run time produces a log entry by default and ignores the exception. (You can disable the log message by setting the property
Ice.Warn.AMICallbackto
zero.)
To inform the Ice run time that you want to receive a callback for the completion of the asynchronous call, you pass the callback instance to the
begin_method:
Java
EmployeesPrx e = ...; MyCallback cb = new MyCallback(); e.begin_getName(99, cb);
This is often written using an anonymous class instead:
Java
EmployeesPrx e = ...; e.begin_getName( 99, new Ice.AsyncCallback() { public void completed(Ice.AsyncResult r) { EmployeesPrx p = (EmployeesPrx)r.getProxy(); try { String name = p.end_getName(r); System.out.println("Name is: " + name); } catch (Ice.LocalException ex) { System.err.println("Exception: " + ex); } } });
An anonymous class is useful particularly for callbacks that do only a small amount of work because the code that starts the call and the code that processes the results are physically close together.
Sharing State Between begin_
and end_
Methods in Java
It is common for the end_method to require access to some state that is established by the code that calls the
begin_method. As an example, consider an application that
asynchronously starts a number of operations and, as each operation completes, needs to update different user interface elements with the results. In this case, the
begin_method knows which user interface element should
receive the update, and the
end_method needs access to that element.
Assuming that we have a
Widgetclass that designates a particular user interface element, you could pass different widgets by storing the widget to be used as a member of your callback class:
Java
public class MyCallback extends Ice.AsyncCallback { public MyCallback(Widget w) { _w = w; } private Widget _w; public void completed(Ice.AsyncResult r) { EmployeesPrx e = (EmployeesPrx)r.getProxy(); try { String name = e.end_getName(r); _w.writeString(name); } catch (Ice.LocalException ex) { System.err.println("Exception is: " + ex); } } }
For this example, we assume that widgets have a
writeStringmethod that updates the relevant UI element.
When you call the
begin_method, you pass the appropriate callback instance to inform the
end_method how to update the display:
Java
EmployeesPrx e = ...; Widget widget1 = ...; Widget widget2 = ...; // Invoke the getName operation with different widget callbacks. e.begin_getName(99, new MyCallback(widget1)); e.begin_getName(24, new MyCallback(widget2));
The callback class provides a simple and effective way for you to pass state between the point where an operation is invoked and the point where its results are processed. Moreover, if you have a number of operations that share common state, you
can pass the same callback instance to multiple invocations. (If you do this, your callback methods may need to use synchronization.)
Type-Safe Completion Callbacks in Java
The generic callback API isnot entirely type-safe:
You must down-cast the return value of
getProxyto the correct proxy type before you can call the
end_method.
You must call the correct
end_method to match the operation called by the
begin_method.
You must remember to catch exceptions when you call the
end_method; if you forget to do this, you will not
know that the operation failed.
slice2javagenerates an additional type-safe API that takes care of these chores for you. To use type-safe callbacks, you must implement a callback class that provides two callback methods:
a
responsemethod that is called if the operation succeeds
an
exceptionmethod that is called if the operation raises an exception
Your callback class must derive from the base class that is generated by
slice2java. The name of this base class is
<module>.Callback_
<interface>_
<operation>.
Here is a callback class for an invocation of the
getNameoperation:
Java
public class MyCallback extends Demo.Callback_Employees_getName { public void response(String name) { System.out.println("Name is: " + name); } public void exception(Ice.LocalException ex) { System.err.println("Exception is: " + ex); } }
The
responsecallback parameters depend on the operation signature. If the operation has non-
voidreturn type, the first parameter of the
responsecallback
is the return value. The return value (if any) is followed by a parameter for each out-parameter of the corresponding Slice operation, in the order of declaration.
The
exceptioncallback is invoked if the invocation fails because of an Ice run time exception. If the Slice operation can also raise user exceptions, your callback class must supply an additional overloading
of
exceptionthat accepts an argument of type
Ice.UserException.
The proxy methods are overloaded to accept this callback instance:
Java
Ice.AsyncResult begin_getName(int number, Callback_Employees_getName __cb); Ice.AsyncResult begin_getName(int number, java.util.Map<String, String> __ctx, Callback_Employees_getName __cb);
You pass the callback to an invocation as you would with the generic API:
Java
EmployeesPrx e = ...; MyCallback cb = new MyCallback(); e.begin_getName(99, cb);
Asynchronous Oneway Invocations in Java
You can invoke operations via oneway proxies asynchronously, provided the operation has voidreturn type, does not have any out-parameters, and does not raise user exceptions. If you call the
begin_method
on a oneway proxy for an operation that returns values or raises a user exception, the
begin_method throws an
IllegalArgumentException.
The callback methods looks exactly as for a twoway invocation. For the generic API, the Ice run time does not call the
completedcallback method unless the invocation raised an exception during the
begin_method
("on the way out"). For the type-safe API, the
responsemethod is never called.
Flow Control in Java
Asynchronous method invocations never block the thread that calls thebegin_method: the Ice run time checks to see whether it can write the request to the local transport. If it can, it does so immediately
in the caller's thread. (In that case,
AsyncResult.sentSynchronouslyreturns true.) Alternatively, if the local transport does not have sufficient buffer space to accept the request, the Ice run time queues the request internally for
later transmission in the background. (In that case,
AsyncResult.sentSynchronouslyreturns false.)
This creates a potential problem: if a client sends many asynchronous requests at the time the server is too busy to keep up with them, the requests pile up in the client-side run time until, eventually, the client runs out of memory.
The API provides a way for you to implement flow control by counting the number of requests that are queued so, if that number exceeds some threshold, the client stops invoking more operations until some of the queued operations have drained out
of the local transport.
For the generic API,
you can override the
sentmethod:
Java
public class MyCallback extends Ice.AsyncCallback { public void completed(Ice.AsyncResult r) { // ... } public void sent(Ice.AsyncResult r) { // ... } }
You inform the Ice run time that you want to be informed when a call has been passed to the local transport as usual:
Java
e.begin_getName(99, new MyCallback());
If the Ice run time can immediately pass the request to the local transport, it does so and invokes the
sentmethod from the thread that calls the
begin_method. On the
other hand, if the run time has to queue the request, it calls the
sentmethod from a different thread once it has written the request to the local transport. In addition, you can find out from the
AsyncResultthat
is returned by the
begin_method whether the request was sent synchronously or was queued, by calling
sentSynchronously.
For the generic API,
the
sentmethod has the following signature:
Java
void sent(Ice.AsyncResult r);
For the type-safe API,
the signature is:
Java
void sent(boolean sentSynchronously);
For the generic API, you can find out whether the request was sent synchronously by calling
sentSynchronouslyon the
AsyncResult. For the type-safe API, the boolean
sentSynchronouslyparameter
provides the same information.
The
sentmethods allow you to limit the number of queued requests by counting the number of requests that are queued and decrementing the count when the Ice run time passes a request to the local transport.
Asynchronous Batch Requests in Java
Applications that send batched requests can either flush a batch explicitly or allowthe Ice run time to flush automatically. The proxy method
ice_flushBatchRequestsperforms an immediate flush using the synchronous invocation model and may block the calling thread until the entire message can be sent.
Ice also provides asynchronous versions of this method so you can flush batch requests asynchronously.
begin_ice_flushBatchRequestsand
end_ice_flushBatchRequestsare proxy methods that flush any batch requests queued by that proxy.
In addition, similar methods are available on the communicator and the
Connectionobject that is returned by
AsyncResult.getConnection. These methods flush batch requests sent via
the same communicator and via the same connection, respectively.
Concurrency Semantics for AMI in Java
The Ice run time always invokes your callback methods from a separate thread, with one exception: it calls the sentcallback from the thread calling the
begin_method
if the request could be sent synchronously. In the
sentcallback, you know which thread is calling the callback by looking at the
sentSynchronouslymember or parameter.
AMI Limitations in Java
AMI invocations cannot be sent using collocated optimization. If you attempt to invoke an AMI operation using a proxy that is configured to use collocationoptimization, the Ice run time raises
CollocationOptimizationExceptionif the servant happens to be collocated; the request is sent normally if the servant is not collocated. You can disable this optimization if necessary.
相关文章推荐
- ICE Manual(Documentation for Ice 3.5)---Java Mapping--Server-Side
- ICE Manual(Documentation for Ice 3.5)---Language Mappings
- ICE Manual(Documentation for Ice 3.5)---Ice Architecture--客户端服务端结构
- ICE Manual(Documentation for Ice 3.5)---Slice Source Files
- ICE Manual(Documentation for Ice 3.5)---Connection Management(Using Connections)
- ICE Manual(Documentation for Ice 3.5)---The Ice Run Time in Detail(Automatic Retries)
- ICE Manual(Documentation for Ice 3.5)---Ice Architecture--调用方式介绍
- ICE Manual(Documentation for Ice 3.5)---Connection Management(Bidirectional Connections)
- ICE Manual(Documentation for Ice 3.5)---IceGrid Architecture
- ICE Manual(Documentation for Ice 3.5)---Connection Management(Connection Closure)
- ICE Manual(Documentation for Ice 3.5)---Slice Lexical Rules
- ICE Manual(Documentation for Ice 3.5)---Ice Architecture--Terminology
- ICE Manual(Documentation for Ice 3.5)---Slice Modules
- ICE Manual(Documentation for Ice 3.5)---The Ice Protocol(Overview)
- ICE Manual(Documentation for Ice 3.5)---The Ice Protocol(Data Encoding)
- ICE Manual(Documentation for Ice 3.5)---The Ice Protocol(Protocol Compression)
- ICE Manual(Documentation for Ice 3.5)---The Ice Protocol(与IIOP对比)
- ICE Manual(Documentation for Ice 3.5)---Slice
- ICE Manual(Documentation for Ice 3.5)---Ice Architecture--异常信息
- ICE Manual(Documentation for Ice 3.5)---The Ice Protocol(与IIOP对比)