Dynamic Dispatch
This page describes the server-side language mappings for the ice_invoke
operation on Blobject servant classes.
On this page:
Synchronous Mapping for ice_invoke
Implementing the dynamic dispatch model requires writing a subclass of an Ice class and defining the ice_invoke
function. We continue using the Compute
interface from our dynamic invocation example to demonstrate the server-side implementation.
ice_invoke
API
The synchronous mapping for ice_invoke
is shown below:
class ComputeI : public Ice::Blobject { public: virtual bool ice_invoke(std::vector<Ice::Byte> inParams, std::vector<Ice::Byte>& outParams, const Ice::Current& current) override; };
An instance of ComputeI
is an Ice object because Blobject
derives from Object
, therefore an instance can be added to an object adapter like any other servant.
class ComputeI : public Ice::BlobjectArray { public: virtual bool ice_invoke(std::pair<const Ice::Byte*, const Ice::Byte*> in, std::vector<Ice::Byte>& out, const Ice::Current& current) override; };
The BlobjectArray
class uses an alternative mapping for sequence
input parameters that avoids the overhead of extra copying. The ice_invoke
function treats the encoded input parameters as a value of type sequence<byte>
.
An instance of ComputeI
is an Ice object because BlobjectArray
derives from Object
, therefore an instance can be added to an object adapter like any other servant.
class ComputeI : public Ice::Blobject { public: virtual bool ice_invoke(const std::vector<Ice::Byte>& inParams, std::vector<Ice::Byte>& outParams, const Ice::Current& current); };
An instance of ComputeI
is an Ice object because Blobject
derives from Object
, therefore an instance can be added to an object adapter like any other servant.
class ComputeI : public Ice::BlobjectArray { public: virtual bool ice_invoke(const std::pair<const Ice::Byte*, const Ice::Byte*>& in, std::vector<Ice::Byte>& out, const Ice::Current& current) = 0; };
The BlobjectArray
class uses an alternative mapping for sequence
input parameters that avoids the overhead of extra copying. The ice_invoke
function treats the encoded input parameters as a value of type sequence<byte>
.
An instance of ComputeI
is an Ice object because BlobjectArray
derives from Object
, therefore an instance can be added to an object adapter like any other servant.
public class ComputeI : Ice.Blobject { public bool ice_invoke(byte[] inParams, out byte[] outParams, Ice.Current current); { // ... } }
An instance of ComputeI
is an Ice object because Blobject
derives from Object
, therefore an instance can be added to an object adapter like any other servant.
public class ComputeI implements com.zeroc.Ice.Blobject { public com.zeroc.Ice.Object.Ice_invokeResult ice_invoke(byte[] inParams, com.zeroc.Ice.Current current) throws com.zeroc.Ice.UserException { // ... } }
An instance of ComputeI
is an Ice object because Blobject
derives from Object
, therefore an instance can be added to an object adapter like any other servant.
public class ComputeI extends Ice.Blobject { public boolean ice_invoke( byte[] inParams, Ice.ByteSeqHolder outParams, Ice.Current current) throws Ice.UserException { // ... } }
An instance of ComputeI
is an Ice object because Blobject
derives from Object
, therefore an instance can be added to an object adapter like any other servant.
@interface ComputeI : ICEBlobject<ICEBlobject> -(BOOL) ice_invoke:(NSData*)inEncaps outEncaps:(NSMutableData**)outEncaps current:(ICECurrent*)current; @end
An instance of ComputeI
is an Ice object because ICEBlobject
derives from ICEObject
, therefore an instance can be added to an object adapter like any other servant.
class ComputeI(Ice.Blobject): def ice_invoke(self, inParams, current): # ...
An instance of ComputeI
is an Ice object because Ice.Blobject
derives from Ice.Object
, therefore an instance can be added to an object adapter like any other servant.
public class ComputeI : Ice.Blobject { func ice_invoke(inEncaps: Data, current: Current) throws -> (ok: Bool, outParams: Data) { ... } }
An instance of ComputeI
is an Ice object and like other Swift servant can be added to an object adapter using the matching Disp
struct Ice.BlobjectDisp
.
Object Operations
For the purposes of this discussion, the implementation of ice_invoke
handles only the add
operation and raises OperationNotExistException
for all other operations. In a real implementation, the servant must also be prepared to receive invocations of the following Object
operations:
string ice_id()
Returns the Slice type ID of the servant's most-derived type.
StringSeq ice_ids()
Returns a sequence of strings representing all of the Slice interfaces supported by the servant, including"::Ice::Object"
.
bool ice_isA(string id)
Returnstrue
if the servant supports the interface denoted by the given Slice type ID, orfalse
otherwise. This operation is invoked by the proxy functioncheckedCast
.
void ice_ping()
Verifies that the object denoted by the identity and facet contained inIce::Current
is reachable.
Implementing ice_invoke
Here is our simplified implementation of ice_invoke
:
bool ComputeI::ice_invoke(std::vector<Ice::Byte> inParams, std::vector<Ice::Byte>& outParams, const Ice::Current& current) { if(current.operation == "add") { auto communicator = current.adapter->getCommunicator(); Ice::InputStream in(communicator, inParams); in.startEncapsulation(); int x, y; in.read(x); in.read(y); in.endEncapsulation(); Ice::OutputStream out(communicator); if(checkOverflow(x, y)) { Calc::Overflow ex(x, y); out.startEncapsulation(); out.writeException(ex); out.endEncapsulation(); out.finished(outParams); return false; } else { out.startEncapsulation(); out.write(x + y); out.endEncapsulation(); out.finished(outParams); return true; } } else { throw Ice::OperationNotExistException(__FILE__, __LINE__, current.id, current.facet, current.operation); } }
bool ComputeI::ice_invoke(const std::vector<Ice::Byte>& inParams, std::vector<Ice::Byte>& outParams, const Ice::Current& current) { if(current.operation == "add") { Ice::CommunicatorPtr communicator = current.adapter->getCommunicator(); Ice::InputStream in(communicator, inParams); in.startEncapsulation(); Ice::Int x, y; in.read(x); in.read(y); in.endEncapsulation(); Ice::OutputStream out(communicator); if(checkOverflow(x, y)) { Calc::Overflow ex(x, y); out.startEncapsulation(); out.writeException(ex); out.endEncapsulation(); out.finished(outParams); return false; } else { out.startEncapsulation(); out.write(x + y); out.endEncapsulation(); out.finished(outParams); return true; } } else { throw Ice::OperationNotExistException(__FILE__, __LINE__, current.id, current.facet, current.operation); } }
public bool ice_invoke(byte[] inParams, out byte[] outParams, Ice.Current current) { if(current.operation.Equals("add")) { Ice.Communicator communicator = current.adapter.getCommunicator(); Ice.InputStream inStream = new Ice.InputStream(communicator, inParams); inStream.startEncapsulation(); int x = inStream.readInt(); int y = inStream.readInt(); inStream.endEncapsulation(); Ice.OutputStream outStream = new Ice.OutputStream(communicator); try { if(checkOverflow(x, y)) { Calc.Overflow ex = new Calc.Overflow(); ex.x = x; ex.y = y; outStream.startEncapsulation(); outStream.writeException(ex); outStream.endEncapsulation(); outParams = outStream.finished(); return false; } else { outStream.startEncapsulation(); outStream.writeInt(x + y); outStream.endEncapsulation(); outParams = outStream.finished(); return true; } } finally { outStream.destroy(); } } else { Ice.OperationNotExistException ex = new Ice.OperationNotExistException(); ex.id = current.id; ex.facet = current.facet; ex.operation = current.operation; throw ex; } }
public com.zeroc.Ice.Object.Ice_invokeResult ice_invoke(byte[] inParams, com.zeroc.Ice.Current current) { if(current.operation.equals("add")) { com.zeroc.Ice.Communicator communicator = current.adapter.getCommunicator(); com.zeroc.Ice.InputStream in = new com.zeroc.Ice.InputStream(communicator, inParams); in.startEncapsulation(); int x = in.readInt(); int y = in.readInt(); in.endEncapsulation(); com.zeroc.Ice.OutputStream out = new com.zeroc.Ice.OutputStream(communicator); com.zeroc.Ice.Object.Ice_invokeResult r = new com.zeroc.Ice.Object.Ice_invokeResult(); if(checkOverflow(x, y)) { Calc.Overflow ex = new Calc.Overflow(); ex.x = x; ex.y = y; out.startEncapsulation(); out.writeException(ex); out.endEncapsulation(); r.outParams = out.finished(); r.returnValue = false; } else { out.startEncapsulation(); out.writeInt(x + y); out.endEncapsulation(); r.outParams = out.finished(); r.returnValue = true; } return r; } else { Ice.OperationNotExistException ex = new Ice.OperationNotExistException(); ex.id = current.id; ex.facet = current.facet; ex.operation = current.operation; throw ex; } }
public boolean ice_invoke( byte[] inParams, Ice.ByteSeqHolder outParams, Ice.Current current) { if(current.operation.equals("add")) { Ice.Communicator communicator = current.adapter.getCommunicator(); Ice.InputStream in = new Ice.InputStream(communicator, inParams); in.startEncapsulation(); int x = in.readInt(); int y = in.readInt(); in.endEncapsulation(); Ice.OutputStream out = new Ice.OutputStream(communicator); if(checkOverflow(x, y)) { Calc.Overflow ex = new Calc.Overflow(); ex.x = x; ex.y = y; out.startEncapsulation(); out.writeException(ex); out.endEncapsulation(); outParams.value = out.finished(); return false; } else { out.startEncapsulation(); out.writeInt(x + y); out.endEncapsulation(); outParams.value = out.finished(); return true; } } else { Ice.OperationNotExistException ex = new Ice.OperationNotExistException(); ex.id = current.id; ex.facet = current.facet; ex.operation = current.operation; throw ex; } }
func ice_invoke(inEncaps: Data, current: Current) throws -> (ok: Bool, outParams: Data) { switch current.operation { case "add": let communicator = try current.adapter!.getCommunicator() let inStream = Ice.InputStream(communicator: communicator, bytes: inEncaps) try inStream.startEncapsulation() let x: Int32 = try inStream.read() let y: Int32 = try inStream.read() try inStream.endEncapsulation(); let outStream = Ice.OutputStream(communicator: communicator) if checkOverflow(x, y) { let ex = Overflow(x: x, y: y) outStream.startEncapsulation() outStream.write(ex) outStream.endEncapsulation() return (ok: false, outParams: outStream.finished()) } else { outStream.startEncapsulation() outStream.write(x + y) outStream.endEncapsulation() return (ok: true, outParams: outStream.finished()) } default: throw Ice.OperationNotExistException(id: current.id, facet: current.facet, operation: current.operation) } }
If an overflow is detected, the code "raises" the Calc::Overflow
user exception by calling writeException
on the output stream and returning false
, otherwise the return value is encoded and the function returns true
.
Asynchronous Mapping for ice_invoke
Ice provides an alternate base class for asynchronous dispatch:
namespace Ice { class BlobjectAsync : public virtual Ice::Object { public: virtual void ice_invokeAsync(std::vector<Byte> inParams, std::function<void(bool, const std::vector<Byte>&)> response, std::function<void(std::exception_ptr)> eptr, const Ice::Current& current) = 0; } }
You need to create a servant class derived from BlobjectAsync
, and override ice_invokeAsync.
Upon a successful invocation, the servant must call response
, passing true
as the first argument and the encapsulated operation result into the second argument. The servant calls response
with false
as the first argument to report a user exception; the second argument is then the encapsulated user exception.
The eptr
function is used to report Ice run-time exceptions and other (non-Ice) C++ exceptions. If you accidentally report a user exception with this function, the caller will receive a UnknownUserException
.
namespace Ice { class BlobjectArrayAsync : public virtual Object { public: virtual void ice_invokeAsync(std::pair<const Ice::Byte*, const Ice::Byte*> inParams, std::function<void(bool, const std::pair<const Ice::Byte*, const Ice::Byte*>&)> response, std::function<void(std::exception_ptr)> eptr, const Ice::Current& current) = 0; }; }
The BlobjectArrayAsync
class uses an alternative mapping for sequence
input parameters that avoids the overhead of extra copying. The ice_invokeAsync
function treats the encoded input parameters as a value of type sequence<byte>
.
You need to create a servant class derived from BlobjectArrayAsync
, and override ice_invokeAsync.
Upon a successful invocation, the servant must call response
, passing true
as the first argument and the encapsulated operation result into the second argument. The servant calls response
with false
as the first argument to report a user exception; the second argument is then the encapsulated user exception.
The eptr
function is used to report Ice run-time exceptions and other (non-Ice) C++ exceptions. If you accidentally report a user exception with this function, the caller will receive a UnknownUserException
.
namespace Ice { class BlobjectAsync : public virtual Ice::Object { public: virtual void ice_invoke_async(const AMD_Object_ice_invokePtr& cb, const std::vector<Ice::Byte>& inParams, const Ice::Current& current) = 0; } }
You need to create a class derived from BlobjectAsync
, and override ice_invoke_async.
The first argument to the servant's member function is a callback object of type Ice::AMD_Object_ice_invoke
, shown here:
namespace Ice { class AMD_Object_ice_invoke : ... { public: virtual void ice_response(bool result, const std::vector<Ice::Byte>& outParams) = 0; virtual void ice_response(bool result, const std::pair<const Ice::Byte*, const Ice::Byte*>& outParams) = 0; virtual void ice_exception(const std::exception&) = 0; virtual void ice_exception() = 0; } }
Upon a successful invocation, the servant must call ice_response
, passing true
as the first argument and the encapsulated operation result into the second argument. The servant calls ice_response
with false
as the first argument to report a user exception; the second argument is then the encapsulated user exception.
ice_exception
is used to report Ice run-time exceptions and other (non-Ice) standard C++ exceptions. If you accidentally report a user exception with ice_exception
, the caller will receive a UnknownUserException
.
namespace Ice { class BlobjectArrayAsync : public virtual Object { public: virtual void ice_invoke_async(const Ice:AMD_Object_ice_invokePtr& cb, const std::pair<const Ice::Byte*, const Ice::Byte*>& inParams, const Ice::Current& current) = 0; }; }
The BlobjectArrayAsync
class uses an alternative mapping for sequence
input parameters that avoids the overhead of extra copying. The ice_invoke_async
function treats the encoded input parameters as a value of type sequence<byte>
.
You need to create a class derived from BlobjectArrayAsync
, and override ice_invoke_async.
The first argument to the servant's member function is a callback object of type Ice::AMD_Object_ice_invoke
, shown here:
namespace Ice { class AMD_Object_ice_invoke : ... { public: virtual void ice_response(bool result, const std::vector<Ice::Byte>& outParams) = 0; virtual void ice_response(bool result, const std::pair<const Ice::Byte*, const Ice::Byte*>& outParams) = 0; virtual void ice_exception(const std::exception&) = 0; virtual void ice_exception() = 0; } }
Upon a successful invocation, the servant must call ice_response
, passing true
as the first argument and the encapsulated operation result into the second argument. The servant calls ice_response
with false
as the first argument to report a user exception; the second argument is then the encapsulated user exception.
ice_exception
is used to report Ice run-time exceptions and other (non-Ice) standard C++ exceptions. If you accidentally report a user exception with ice_exception
, the caller will receive a UnknownUserException
.
namespace Ice { public struct Object_Ice_invokeResult { public Object_Ice_invokeResult(bool returnValue, byte[] outEncaps); public bool returnValue; public byte[] outEncaps; } public abstract class BlobjectAsync : Ice.ObjectImpl { public abstract Task<Object_Ice_invokeResult> ice_invokeAsync(byte[] inEncaps, Current current); } }
To implement asynchronous dynamic dispatch, a server must subclass BlobjectAsync
and override ice_invokeAsync
.
The return value for successful completion, or for a user exception, is a Task
whose result is an instance of Object_Ice_invokeResult
.
The servant may optionally raise a user exception directly and the Ice run time will marshal it for you.
package com.zeroc.Ice; public interface BlobjectAsync extends com.zeroc.Ice.Object { java.util.concurrent.CompletionStage<Object.Ice_invokeResult> ice_invokeAsync(byte[] inEncaps, Current current) throws UserException; }
To implement asynchronous dynamic dispatch, a server must implement BlobjectAsync
and define ice_invokeAsync
.
The return value for successful completion, or for a user exception, is a CompletionStage
whose result is an instance of Object.Ice_invokeResult
:
package com.zeroc.Ice; public interface Object { public class Ice_invokeResult { public Ice_invokeResult() { } public Ice_invokeResult(boolean returnValue, byte[] outParams) { this.returnValue = returnValue; this.outParams = outParams; } public boolean returnValue; public byte[] outParams; } ... }
The servant may optionally raise a user exception directly and the Ice run time will marshal it for you.
package Ice; public abstract class BlobjectAsync extends Ice.ObjectImpl { public abstract void ice_invoke_async( Ice.AMD_Object_ice_invoke cb, byte[] inParams, Ice.Current current); // ... }
To implement asynchronous dynamic dispatch, a server must subclass BlobjectAsync
and override ice_invoke_async
.
As with any other asynchronous operation, the first argument to the servant's member function is always a callback object. In this case, the callback object is of type Ice.AMD_Object_ice_invoke
, shown here:
package Ice; public interface AMD_Object_ice_invoke { void ice_response(boolean result, byte[] outParams); void ice_exception(java.lang.Exception ex); }
Upon a successful invocation, the servant must invoke ice_response
on the callback object, passing true
as the first argument and encoding the encapsulated operation results into outParams
. To report a user exception, the servant invokes ice_response
with false
as the first argument and the encapsulated form of the exception in outParams
. Alternatively, the servant can pass a user exception instance to ice_exception
.
class Blobject(Object): def ice_invoke(self, inParams, current): # ...
To implement asynchronous dynamic dispatch, a server must subclass Blobject
and implement ice_invoke
.
The return value for successful completion, or for a user exception, is a Future
whose result is a tuple consisting of (ok, outParams)
, where ok
is True
if the invocation completed successfully and outParams
contains the encapsulated output parameters, or False
if the invocation resulted in a user exception and outParams
contains the encapsulated exception.
The servant may optionally raise a user exception directly and the Ice run time will marshal it for you.
/// Base protocol for dynamic asynchronous dispatch servants. public protocol BlobjectAsync { func ice_invokeAsync(inEncaps: Data, current: Current) -> Promise<(ok: Bool, outParams: Data)> }
To implement asynchronous dynamic dispatch, a server must implement the BlobjectAsync
protocol.
The return value is a PromiseKit
promise that must be completed with a tuple, that has two elements, the first element named ok is a boolean that is true if the operation completed successfully or false if it raised a user exception (in this case, the second element outParams
contains the encoded user exception), the second element named outParams
contains the encoded out-parameters and return value for the operation, Ice run-time exception can be reported using the PromiseKit returned promise.