Dynamic Invocation
6 min read
5 min read
5 min read
5 min read
5 min read
A typed proxy marshals the parameters of an operation and unmarshals its results with code generated from the Slice definition of the operation. The ice_invokeice_invokeice_invokeice_invokeice_invokeice_invoke method of ObjectPrxObjectPrxObjectPrxObjectPrxObjectPrxObjectPrxObjectPrxObjectPrx invokes an operation by name instead: the application supplies the marshaled in-parameters and receives the marshaled reply. An application that forwards requests it does not unmarshal, or that calls operations without the generated code for the target interface, uses ice_invoke.
Invoking an Operation
ice_invoke accepts the following arguments:
- the name of the operation
- the operation mode,
NormalorIdempotent - an encapsulation holding the marshaled in-parameters
- an optional request context
Ice::ObjectPrx provides a synchronous ice_invoke and two asynchronous ice_invokeAsync overloads, one returning a future and one accepting callbacks:
namespace Ice{ class ObjectPrx { public: bool ice_invoke( std::string_view operation, OperationMode mode, const std::vector<std::byte>& inParams, std::vector<std::byte>& outParams, const Context& context = noExplicitContext) const;
std::future<std::tuple<bool, std::vector<std::byte>>> ice_invokeAsync( std::string_view operation, OperationMode mode, const std::vector<std::byte>& inParams, const Context& context = noExplicitContext) const;
std::function<void()> ice_invokeAsync( std::string_view operation, OperationMode mode, const std::vector<std::byte>& inParams, std::function<void(bool, std::vector<std::byte>)> response, std::function<void(std::exception_ptr)> ex = nullptr, std::function<void(bool)> sent = nullptr, const Context& context = noExplicitContext) const; ... };}Each function also has an overload that takes the in-parameters as a std::pair<const std::byte*, const std::byte*>; see the API reference.
Ice.ObjectPrx provides a synchronous ice_invoke and an asynchronous ice_invokeAsync:
namespace Ice;
public interface ObjectPrx{ bool ice_invoke( string operation, OperationMode mode, byte[] inEncaps, out byte[] outEncaps, Dictionary<string, string>? context = null);
Task<Object_Ice_invokeResult> ice_invokeAsync( string operation, OperationMode mode, byte[] inEncaps, Dictionary<string, string>? context = null, IProgress<bool>? progress = null, CancellationToken cancel = default); ...}
public record struct Object_Ice_invokeResult(bool returnValue, byte[] outEncaps);ObjectPrx provides a synchronous ice_invoke and an asynchronous ice_invokeAsync, each with an overload that accepts a request context:
package com.zeroc.Ice;
public interface ObjectPrx { Object.Ice_invokeResult ice_invoke(String operation, OperationMode mode, byte[] inParams);
Object.Ice_invokeResult ice_invoke( String operation, OperationMode mode, byte[] inParams, Map<String, String> context);
CompletableFuture<Object.Ice_invokeResult> ice_invokeAsync( String operation, OperationMode mode, byte[] inParams);
CompletableFuture<Object.Ice_invokeResult> ice_invokeAsync( String operation, OperationMode mode, byte[] inParams, Map<String, String> context); ...}The result holds the success flag in its returnValue field and the encapsulation in its outParams field.
Ice.ObjectPrx provides a synchronous ice_invoke and an asynchronous ice_invokeAsync:
class ObjectPrx: def ice_invoke( self, operation: str, mode: OperationMode, inParams: bytes, ctx: dict[str, str] | None = None ) -> tuple[bool, bytes]: ...
def ice_invokeAsync( self, operation: str, mode: OperationMode, inParams: bytes, ctx: dict[str, str] | None = None ) -> Awaitable[tuple[bool, bytes]]: ...ObjectPrx provides an asynchronous ice_invoke:
extension ObjectPrx { public func ice_invoke( operation: String, mode: OperationMode, inEncaps: Data, context: Context? = nil ) async throws -> (ok: Bool, outEncaps: Data) ...}Operation Mode
The operation mode is Idempotent for a Slice operation declared idempotent, and Normal otherwise. The Ice runtime uses this mode to decide whether it can retry an invocation that fails after the request was sent, and sends the mode with the request. Pass Idempotent when you know the operation is idempotent, or when you forward a request that arrived with this mode.
Marshaling the In-Parameters
You marshal the in-parameters into an encapsulation: the required parameters in the order of their declaration, followed by the optional parameters in the order of their tags, each marshaled as described in Ice Encoding. For an operation without in-parameters, you can pass an empty byte sequence; the Ice runtime then sends an empty encapsulation.
You marshal the in-parameters with an Ice::OutputStream. The following example invokes the greet operation of the Greeter interface, which takes a string parameter:
Ice::ObjectPrx greeter{communicator, "greeter:tcp -h localhost -p 4061"};
Ice::OutputStream outputStream{communicator};outputStream.startEncapsulation();outputStream.write("alice");outputStream.endEncapsulation();std::vector<std::byte> inParams;outputStream.finished(inParams);
std::vector<std::byte> outParams;bool ok = greeter.ice_invoke("greet", Ice::OperationMode::Normal, inParams, outParams);You marshal the in-parameters with an OutputStream. The following example invokes the greet operation of the Greeter interface, which takes a string parameter:
ObjectPrx greeter = ObjectPrxHelper.createProxy( communicator, "greeter:tcp -h localhost -p 4061");
var outputStream = new OutputStream(communicator);outputStream.startEncapsulation();outputStream.writeString("alice");outputStream.endEncapsulation();byte[] inEncaps = outputStream.finished();
Object_Ice_invokeResult result = await greeter.ice_invokeAsync( "greet", OperationMode.Normal, inEncaps);You marshal the in-parameters with an OutputStream. The following example invokes the greet operation of the Greeter interface, which takes a string parameter:
ObjectPrx greeter = ObjectPrx.createProxy(communicator, "greeter:tcp -h localhost -p 4061");
var outputStream = new OutputStream(communicator);outputStream.startEncapsulation();outputStream.writeString("alice");outputStream.endEncapsulation();byte[] inParams = outputStream.finished();
com.zeroc.Ice.Object.Ice_invokeResult result = greeter.ice_invoke("greet", OperationMode.Normal, inParams);The Python mapping has no public stream API, so a Python application passes an encapsulation that it received already marshaled, such as the in-parameters of a request that it forwards.
proxy = Ice.ObjectPrx(communicator, "greeter:tcp -h localhost -p 4061")ok, outParams = await proxy.ice_invokeAsync("greet", Ice.OperationMode.Normal, inParams)You marshal the in-parameters with an OutputStream. The following example invokes the greet operation of the Greeter interface, which takes a string parameter:
let greeter = try makeProxy( communicator: communicator, proxyString: "greeter:tcp -h localhost -p 4061", type: ObjectPrx.self)
let outputStream = OutputStream(communicator: communicator)outputStream.startEncapsulation()outputStream.write("alice")outputStream.endEncapsulation()let inEncaps = outputStream.finished()
let (ok, outEncaps) = try await greeter.ice_invoke( operation: "greet", mode: .normal, inEncaps: inEncaps)Input and Output Streams shows how to read and write each kind of Slice type with these streams.
Reading the Reply
For a twoway invocation, ice_invoke returns a success flag and an encapsulation:
- When the flag is
true, the operation completed successfully, and the encapsulation holds the required out-parameters, followed by the required return value, followed by the optional out-parameters and optional return value in the order of their tags. - When the flag is
false, the operation completed with a user exception, and the encapsulation holds the marshaled user exception.
For any other failure, ice_invoke throws a local or dispatch exception.
For a oneway or datagram invocation, ice_invoke returns true and an empty byte sequence once the transport accepts the request. For a batch oneway or batch datagram invocation, it returns true and an empty byte sequence once it adds the request to the batch; the Ice runtime sends the request when it flushes the batch.
You unmarshal the reply with an Ice::InputStream. throwException unmarshals a user exception and throws it:
Ice::InputStream inputStream{communicator, outParams};inputStream.startEncapsulation();if (ok){ std::string greeting; inputStream.read(greeting); inputStream.endEncapsulation();}else{ try { inputStream.throwException(); } catch (const Ice::UserException&) { inputStream.endEncapsulation(); throw; }}With the callback overload of ice_invokeAsync, the Ice runtime does not call response for a oneway or datagram invocation: it calls sent once the transport accepts the request. When it adds a request to a batch, it calls none of the callbacks.
You unmarshal the reply with an InputStream. throwException unmarshals a user exception and throws it:
var inputStream = new InputStream(communicator, result.outEncaps);inputStream.startEncapsulation();if (result.returnValue){ string greeting = inputStream.readString(); inputStream.endEncapsulation();}else{ try { inputStream.throwException(); } catch (UserException) { inputStream.endEncapsulation(); throw; }}You unmarshal the reply with an InputStream. throwException unmarshals a user exception and throws it:
var inputStream = new InputStream(communicator, result.outParams);inputStream.startEncapsulation();if (result.returnValue) { String greeting = inputStream.readString(); inputStream.endEncapsulation();} else { try { inputStream.throwException(); } catch (UserException ex) { inputStream.endEncapsulation(); throw ex; }}You unmarshal the reply with an InputStream. throwException unmarshals a user exception and throws it:
let inputStream = InputStream(communicator: communicator, bytes: outEncaps)try inputStream.startEncapsulation()if ok { let greeting: String = try inputStream.read() try inputStream.endEncapsulation()} else { do { try inputStream.throwException() } catch let error as UserException { try inputStream.endEncapsulation() throw error }}Forwarding Requests
An application can forward the requests that its object adapter receives with ice_invoke, without unmarshaling their parameters. The forwarding code passes the encapsulation of the in-parameters from the incoming request to ice_invoke on the target proxy, and builds the response from the success flag and the encapsulation that ice_invoke returns. The operation name, mode and context of the outgoing request are the forwarder's to set: a forwarder usually passes on those of the incoming request, and an intermediary such as Glacier2 adds or removes context entries along the way.
A forwarding dispatcher derives from Ice::Object and overrides dispatch.
The Ice forwarder demo provides a complete example of a forwarding server in C++.
A forwarding dispatcher implements Ice.Object and its dispatchAsync method.
The Ice forwarder demo provides a complete example of a forwarding server in C#.
A forwarding dispatcher implements com.zeroc.Ice.Object and its dispatch method.
The Ice forwarder demo provides a complete example of a forwarding server in Java.
A forwarding servant derives from Ice.Blobject and implements its ice_invoke method, which receives the encapsulation of the in-parameters and returns the success flag and the encapsulation of the reply, or an awaitable for them, such as the one that ice_invokeAsync returns.
A forwarding dispatcher adopts the Dispatcher protocol.
The Ice forwarder demo provides a complete example of a forwarding server in Swift.