| // Copyright 2015 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package io.v.impl.google.rpc; |
| |
| import com.google.common.util.concurrent.ListenableFuture; |
| |
| import io.v.impl.google.ListenableFutureCallback; |
| import io.v.v23.OptionDefs; |
| import io.v.v23.Options; |
| import io.v.v23.context.VContext; |
| import io.v.v23.rpc.Callback; |
| import io.v.v23.rpc.Client; |
| import io.v.v23.rpc.ClientCall; |
| import io.v.v23.security.Authorizer; |
| import io.v.v23.security.VSecurity; |
| import io.v.v23.verror.VException; |
| import io.v.v23.vom.VomUtil; |
| |
| import java.lang.reflect.Type; |
| |
| /** |
| * An implementation of the {@link Client} interface that calls to native code for most of its |
| * functionalities. |
| */ |
| public class ClientImpl implements Client { |
| private final long nativeRef; |
| |
| private native void nativeStartCall(long nativeRef, VContext context, |
| String name, String method, byte[][] vomArgs, |
| Authorizer nameResolutionAuthorizer, |
| Authorizer serverAuthorizer, |
| Callback<ClientCall> callback); |
| private native void nativeClose(long nativeRef); |
| private native void nativeFinalize(long nativeRef); |
| |
| private ClientImpl(long nativeRef) { |
| this.nativeRef = nativeRef; |
| } |
| |
| private boolean shouldSkipServerAuth(Options opts) { |
| return !opts.has(OptionDefs.SKIP_SERVER_ENDPOINT_AUTHORIZATION) |
| ? false |
| : opts.get(OptionDefs.SKIP_SERVER_ENDPOINT_AUTHORIZATION, Boolean.class); |
| } |
| |
| private Authorizer nameResolutionAuthorizer(Options opts) { |
| if (opts.has(OptionDefs.SKIP_SERVER_ENDPOINT_AUTHORIZATION) && |
| opts.get(OptionDefs.SKIP_SERVER_ENDPOINT_AUTHORIZATION, Boolean.class)) |
| return VSecurity.newAllowEveryoneAuthorizer(); |
| |
| return !opts.has(OptionDefs.NAME_RESOLUTION_AUTHORIZER) |
| ? null |
| : opts.get(OptionDefs.NAME_RESOLUTION_AUTHORIZER, Authorizer.class); |
| } |
| |
| private Authorizer serverAuthorizer(Options opts) { |
| if (opts.has(OptionDefs.SKIP_SERVER_ENDPOINT_AUTHORIZATION) && |
| opts.get(OptionDefs.SKIP_SERVER_ENDPOINT_AUTHORIZATION, Boolean.class)) |
| return VSecurity.newAllowEveryoneAuthorizer(); |
| |
| return !opts.has(OptionDefs.SERVER_AUTHORIZER) |
| ? null |
| : opts.get(OptionDefs.SERVER_AUTHORIZER, Authorizer.class); |
| } |
| |
| // Implement io.v.v23.rpc.Client. |
| @Override |
| public ListenableFuture<ClientCall> startCall( |
| VContext ctx, String name, String method, Object[] args, Type[] argTypes) { |
| return startCall(ctx, name, method, args, argTypes, null); |
| } |
| @Override |
| public ListenableFuture<ClientCall> startCall(VContext ctx, String name, String method, |
| Object[] args, Type[] argTypes, Options opts) { |
| ListenableFutureCallback<ClientCall> callback = new ListenableFutureCallback<>(); |
| if (opts == null) { |
| opts = new Options(); |
| } |
| try { |
| checkStartCallArgs(name, method, args, argTypes); |
| nativeStartCall(nativeRef, ctx, name, getMethodName(method), |
| getEncodedVomArgs(args, argTypes), |
| nameResolutionAuthorizer(opts), serverAuthorizer(opts), callback); |
| } catch (VException e) { |
| callback.onFailure(e); |
| } |
| return callback.getFuture(ctx); |
| } |
| |
| private String getMethodName(String method) { |
| return Character.toUpperCase(method.charAt(0)) + method.substring(1); |
| } |
| |
| /** |
| * Asserts that the given parameters are valid for {@code startCall} and throws {@link |
| * VException} if they are not. |
| */ |
| private void checkStartCallArgs(String name, String method, Object[] args, Type[] argTypes) |
| throws VException { |
| if ("".equals(method)) { |
| throw new VException(String.format("Empty method name invoked on object %s", name)); |
| } |
| if (args.length != argTypes.length) { |
| throw new VException(String.format( |
| "Argument count (%d) doesn't match type count (%d) for method %s of object %s", |
| args.length, argTypes.length, name, method)); |
| } |
| } |
| |
| private byte[][] getEncodedVomArgs(Object[] args, Type[] argTypes) throws VException { |
| // VOM-encode all input arguments, individually. |
| byte[][] vomArgs = new byte[args.length][]; |
| for (int i = 0; i < args.length; ++i) { |
| vomArgs[i] = VomUtil.encode(args[i], argTypes[i]); |
| } |
| return vomArgs; |
| } |
| |
| @Override |
| public void close() { |
| nativeClose(nativeRef); |
| } |
| // Implement java.lang.Object. |
| @Override |
| public boolean equals(Object other) { |
| if (this == other) return true; |
| if (other == null) return false; |
| if (getClass() != other.getClass()) return false; |
| return nativeRef == ((ClientImpl) other).nativeRef; |
| } |
| @Override |
| public int hashCode() { |
| return Long.valueOf(nativeRef).hashCode(); |
| } |
| @Override |
| protected void finalize() { |
| nativeFinalize(nativeRef); |
| } |
| } |