ProxyBuilder

Using ProxyBuilder is the easiest way to make calls to the Internet Computer using the IC4J Agent. ProxyBuilder will use the Java interface with IC4J annotations which represent the Internet Computer Canister.

The Internet Computer Canister and its Methods will create a proxy object which implements the defined interface.

ProxyBuilder will then provide all Candid type serialization and deserialization steps and the execution of specified canister methods.

Here is an example using Motoko canister with one QUERY and one UPDATE method.

main.mo
actor {
    stable var name = "Me";

    public func greet(value : Text) : async Text {
        name := value;
        return "Hello, " # name # "!";
    };

    public shared query func peek() : async Text {
        return name;
    };    
};

The Java Proxy interface for this canister looks like this.

HelloWorldProxy.java
public interface HelloWorldProxy {	
	@UPDATE
	@Name("greet")
	@Waiter(timeout = 30)
	public CompletableFuture<String> greet(@Argument(Type.TEXT)String name);
	
	@QUERY
	@Name("peek")
	public String peek();
}

To define if the method is QUERY or UPDATE use @QUERY or @UPDATE annotations.

The annotation @Name is optional, but if not specified, the ProxyBuilder will implicitly use the name of the Java method.

For the UPDATE method the developer can explicitly define the Waiter object with @Waiter annotation, which will check the state of the UPDATE operation in certain intervals, defined by the sleep property. The default value for the sleep property is set to 5 seconds.

The developer can also define the timeout property, to specify when the Waiter should keep checking the state.

The default value for the timeout property is set to 60 seconds. If the annotation is not specified, the ProxyBuilder will automatically set default values.

For Method parameters the developer can also set a Candid type of the argument with the @Argument annotation. If this annotation is not defined, the ProxyBuilder will use the default Java to Candid mapping.

Here is an example of how to create a proxy object in Java code.

Main.java
Agent agent = new AgentBuilder().transport(transport).identity(identity).build();			
			
HelloWorldProxy helloWorldProxy = ProxyBuilder.create(agent, Principal.fromString(icCanister))
					.getProxy(HelloWorldProxy.class);rr

The ProxyBuilder Create Method will accept 2 arguments, Agent and Principal.

The getProxy Method will use the proxy interface class as an argument.

The call to the canister can now be completed by calling any other Java function.

String output = helloWorldProxy.peek();

String value = "world";			
CompletableFuture<String> proxyResponse = helloWorldProxy.greet(value);
output = proxyResponse.get();

The UPDATE function call is executed asynchronously, returning the Java CompletableFuture result.

The full source code of this sample can be found here.

LoanBroker.java
@Agent(identity = @Identity(type = IdentityType.BASIC, pem_file = "/cert/Ed25519_identity.pem"), transport = @Transport(url = "http://localhost:8001"))
@Canister("rrkah-fqaaa-aaaaa-aaaaq-cai")
@EffectiveCanister("rrkah-fqaaa-aaaaa-aaaaq-cai")
public interface LoanBroker {
	@UPDATE
	@Name("apply")
	@Waiter(timeout = 30)
	@ResponseClass(LoanOffer.class)
	public CompletableFuture<LoanOffer> apply(@Argument(Type.RECORD)LoanApplication application);
}

Developers can optionally use the following additional annotations.

Use the @Agent annotation to define Identity and Transport properties directly in the proxy interface.

Use the @Canister annotation to define canister id.

Use the @EffectiveCanister annotation to define effective canister id.

If the Complex Type Response is being used with Java class needing to be defined, and deserializing is necessary, use the @ResponseClass annotation.

Last updated