blob: 6cb2e7fe27802cc714f5f832e2890fc52df69122 [file] [log] [blame]
// 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 java
import (
"bytes"
"fmt"
"log"
"path"
"v.io/x/ref/lib/vdl/compile"
"v.io/x/ref/lib/vdl/vdlutil"
)
const clientImplTmpl = header + `
// Source(s): {{ .Source }}
package {{ .PackagePath }};
/**
* Implementation of the {@link {{ .ServiceName }}Client} interface.
*/
final class {{ .ServiceName }}ClientImpl implements {{ .FullServiceName }}Client {
private final io.v.v23.rpc.Client client;
private final java.lang.String vName;
{{/* Define fields to hold each of the embedded object impls*/}}
{{ range $embed := .Embeds }}
{{/* e.g. private final com.somepackage.ArithClient implArith; */}}
private final {{ $embed.FullName }}Client impl{{ $embed.Name }};
{{ end }}
/**
* Creates a new instance of {@link {{ .ServiceName }}ClientImpl}.
*
* @param client Vanadium client
* @param vName remote server name
*/
public {{ .ServiceName }}ClientImpl(io.v.v23.rpc.Client client, java.lang.String vName) {
this.client = client;
this.vName = vName;
{{/* Initialize the embeded impls */}}
{{ range $embed := .Embeds }}
{
io.v.v23.Options opts = new io.v.v23.Options();
opts.set(io.v.v23.OptionDefs.CLIENT, client);
this.impl{{ $embed.Name }} = {{ $embed.FullName }}ClientFactory.get{{ $embed.Name }}Client(vName, opts);
}
{{ end }}
}
private io.v.v23.rpc.Client getClient(io.v.v23.context.VContext context) {
return this.client != null ? client : io.v.v23.V.getClient(context);
}
// Methods from interface {{ .ServiceName }}Client.
{{/* Iterate over methods defined directly in the body of this service */}}
{{ range $method := .Methods }}
{{/* The optionless overload simply calls the overload with options */}}
@Override
public {{ $method.RetType }} {{ $method.Name }}(io.v.v23.context.VContext _context{{ $method.DeclarationArgs }}) {
return {{ $method.Name }}(_context{{ $method.CallingArgsLeadingComma }}, null);
}
@Override
public {{ $method.RetType }} {{ $method.Name }}(final io.v.v23.context.VContext _context{{ $method.DeclarationArgs }}, io.v.v23.Options _opts) {
{{/* Start the vanadium call */}}
// Start the call.
java.lang.Object[] _args = new java.lang.Object[]{ {{ $method.CallingArgs }} };
java.lang.reflect.Type[] _argTypes = new java.lang.reflect.Type[]{ {{ $method.CallingArgTypes }} };
final com.google.common.util.concurrent.ListenableFuture<io.v.v23.rpc.ClientCall> _callFuture = getClient(_context).startCall(_context, this.vName, "{{ $method.Name }}", _args, _argTypes, _opts);
{{ if $method.NotStreaming }}
return io.v.v23.VFutures.withUserLandChecks(_context, com.google.common.util.concurrent.Futures.transform(_callFuture, new com.google.common.util.concurrent.AsyncFunction<io.v.v23.rpc.ClientCall, {{ $method.OutArgType }}>() {
@Override
public com.google.common.util.concurrent.ListenableFuture<{{ $method.OutArgType }}> apply(final io.v.v23.rpc.ClientCall _call) throws Exception {
{{ if $method.IsVoid }}
java.lang.reflect.Type[] _resultTypes = new java.lang.reflect.Type[]{};
{{ else }} {{/* else $method.IsVoid */}}
java.lang.reflect.Type[] _resultTypes = new java.lang.reflect.Type[]{
{{ range $outArg := $method.OutArgs }}
{{ $outArg.ReflectType }},
{{ end }}
};
{{ end }} {{/* end if $method.IsVoid */}}
// Finish the call.
return com.google.common.util.concurrent.Futures.transform(_call.finish(_resultTypes), new com.google.common.base.Function<Object[], {{ $method.OutArgType }}>() {
@Override
public {{ $method.OutArgType }} apply(Object[] _results) {
{{ if $method.IsVoid }}
return null;
{{ else if $method.MultipleReturn }}
{{ $method.DeclaredObjectRetType }} _ret = new {{ $method.DeclaredObjectRetType }}();
{{ range $i, $outArg := $method.OutArgs }}
_ret.{{ $outArg.FieldName }} = ({{ $outArg.Type }})_results[{{ $i }}];
{{ end }} {{/* end range over outargs */}}
return _ret;
{{ else }} {{/* else if $method.MultipleReturn */}}
return ({{ $method.DeclaredObjectRetType }})_results[0];
{{ end }} {{/* end if $method.IsVoid */}}
}
});
}
}));
{{else }} {{/* else $method.NotStreaming */}}
return new io.v.v23.vdl.ClientStream<{{ $method.SendType }}, {{ $method.RecvType }}, {{ $method.DeclaredObjectRetType }}>() {
@Override
public com.google.common.util.concurrent.ListenableFuture<Void> send(final {{ $method.SendType }} item) {
final java.lang.reflect.Type type = {{ $method.StreamSendReflectType }};
return io.v.v23.VFutures.withUserLandChecks(_context, com.google.common.util.concurrent.Futures.transform(_callFuture, new com.google.common.util.concurrent.AsyncFunction<io.v.v23.rpc.ClientCall, Void>() {
@Override
public com.google.common.util.concurrent.ListenableFuture<Void> apply(final io.v.v23.rpc.ClientCall _call) throws Exception {
return _call.send(item, type);
}
}));
}
@Override
public com.google.common.util.concurrent.ListenableFuture<Void> close() {
return io.v.v23.VFutures.withUserLandChecks(_context, com.google.common.util.concurrent.Futures.transform(_callFuture, new com.google.common.util.concurrent.AsyncFunction<io.v.v23.rpc.ClientCall, Void>() {
@Override
public com.google.common.util.concurrent.ListenableFuture<Void> apply(final io.v.v23.rpc.ClientCall _call) throws Exception {
return _call.closeSend();
}
}));
}
@Override
public com.google.common.util.concurrent.ListenableFuture<{{ $method.RecvType }}> recv() {
final java.lang.reflect.Type recvType = {{ $method.StreamRecvReflectType }};
return io.v.v23.VFutures.withUserLandChecks(_context, com.google.common.util.concurrent.Futures.transform(_callFuture, new com.google.common.util.concurrent.AsyncFunction<io.v.v23.rpc.ClientCall, {{ $method.RecvType }}>() {
@Override
public com.google.common.util.concurrent.ListenableFuture<{{ $method.RecvType }}> apply(final io.v.v23.rpc.ClientCall _call) throws Exception {
return com.google.common.util.concurrent.Futures.transform(_call.recv(recvType), new com.google.common.base.Function<Object, {{ $method.RecvType }}>() {
@Override
public {{ $method.RecvType }} apply(Object result) {
return ({{ $method.RecvType }}) result;
}
});
}
}));
}
@Override
public com.google.common.util.concurrent.ListenableFuture<{{ $method.DeclaredObjectRetType }}> finish() {
{{ if $method.IsVoid }}
final java.lang.reflect.Type[] resultTypes = new java.lang.reflect.Type[]{};
{{ else }} {{/* else $method.IsVoid */}}
final java.lang.reflect.Type[] resultTypes = new java.lang.reflect.Type[]{
{{ range $outArg := $method.OutArgs }}
{{ $outArg.ReflectType }},
{{ end }}
};
{{ end }} {{/* end if $method.IsVoid */}}
return io.v.v23.VFutures.withUserLandChecks(_context, com.google.common.util.concurrent.Futures.transform(_callFuture, new com.google.common.util.concurrent.AsyncFunction<io.v.v23.rpc.ClientCall, {{ $method.DeclaredObjectRetType }}>() {
@Override
public com.google.common.util.concurrent.ListenableFuture<{{ $method.DeclaredObjectRetType }}> apply(final io.v.v23.rpc.ClientCall _call) throws Exception {
return com.google.common.util.concurrent.Futures.transform(_call.finish(resultTypes), new com.google.common.base.Function<Object[], {{ $method.DeclaredObjectRetType }}>() {
@Override
public {{ $method.DeclaredObjectRetType }} apply(Object[] _results) {
{{ if $method.IsVoid }}
return null;
{{ else if $method.MultipleReturn }}
{{ $method.DeclaredObjectRetType }} _ret = new {{ $method.DeclaredObjectRetType }}();
{{ range $i, $outArg := $method.OutArgs }}
_ret.{{ $outArg.FieldName }} = ({{ $outArg.Type }})_results[{{ $i }}];
{{ end }} {{/* end range over outargs */}}
return _ret;
{{ else }} {{/* else if $method.MultipleReturn */}}
return ({{ $method.DeclaredObjectRetType }})_results[0];
{{ end }} {{/* end if $method.IsVoid */}}
}
});
}
}));
}
};
{{ end }}{{/* end if $method.NotStreaming */}}
}
{{ end }}{{/* end range over methods */}}
{{/* Iterate over methods from embeded services and generate code to delegate the work */}}
{{ range $eMethod := .EmbedMethods }}
@Override
public {{ $eMethod.RetType }} {{ $eMethod.Name }}(io.v.v23.context.VContext _context{{ $eMethod.DeclarationArgs }}) {
{{/* e.g. return this.implArith.cosine(context, [args]) */}}
return this.impl{{ $eMethod.IfaceName }}.{{ $eMethod.Name }}(_context{{ $eMethod.CallingArgsLeadingComma }});
}
@Override
public {{ $eMethod.RetType }} {{ $eMethod.Name }}(io.v.v23.context.VContext _context{{ $eMethod.DeclarationArgs }}, io.v.v23.Options _opts) {
{{/* e.g. return this.implArith.cosine(_context, [args], options) */}}
return this.impl{{ $eMethod.IfaceName }}.{{ $eMethod.Name }}(_context{{ $eMethod.CallingArgsLeadingComma }}, _opts);
}
{{ end }}
}
`
type clientImplMethodOutArg struct {
FieldName string
ReflectType string
Type string
}
type clientImplMethod struct {
CallingArgs string
CallingArgTypes string
CallingArgsLeadingComma string
DeclarationArgs string
DeclaredObjectRetType string
IsVoid bool
MultipleReturn bool
Name string
NotStreaming bool
OutArgs []clientImplMethodOutArg
OutArgType string
StreamRecvReflectType string
RecvType string
RetType string
StreamSendReflectType string
SendType string
ServiceName string
}
type clientImplEmbedMethod struct {
CallingArgsLeadingComma string
DeclarationArgs string
IfaceName string
Name string
RetType string
}
type clientImplEmbed struct {
Name string
FullName string
}
func processClientImplMethod(iface *compile.Interface, method *compile.Method, env *compile.Env) clientImplMethod {
outArgs := make([]clientImplMethodOutArg, len(method.OutArgs))
for i := 0; i < len(method.OutArgs); i++ {
if method.OutArgs[i].Name != "" {
outArgs[i].FieldName = vdlutil.FirstRuneToLower(method.OutArgs[i].Name)
} else {
outArgs[i].FieldName = fmt.Sprintf("ret%d", i+1)
}
outArgs[i].Type = javaType(method.OutArgs[i].Type, true, env)
outArgs[i].ReflectType = javaReflectType(method.OutArgs[i].Type, env)
}
return clientImplMethod{
CallingArgs: javaCallingArgStr(method.InArgs, false),
CallingArgTypes: javaCallingArgTypeStr(method.InArgs, env),
CallingArgsLeadingComma: javaCallingArgStr(method.InArgs, true),
DeclarationArgs: javaDeclarationArgStr(method.InArgs, env, true),
DeclaredObjectRetType: clientInterfaceNonStreamingOutArg(iface, method, true, env),
IsVoid: len(method.OutArgs) < 1,
MultipleReturn: len(method.OutArgs) > 1,
Name: vdlutil.FirstRuneToLower(method.Name),
NotStreaming: !isStreamingMethod(method),
OutArgs: outArgs,
OutArgType: clientInterfaceOutArg(iface, method, true, env),
StreamRecvReflectType: javaReflectType(method.OutStream, env),
RecvType: javaType(method.OutStream, true, env),
RetType: clientInterfaceRetType(iface, method, env),
StreamSendReflectType: javaReflectType(method.InStream, env),
SendType: javaType(method.InStream, true, env),
ServiceName: vdlutil.FirstRuneToUpper(iface.Name),
}
}
func processClientImplEmbedMethod(iface *compile.Interface, embedMethod *compile.Method, env *compile.Env) clientImplEmbedMethod {
return clientImplEmbedMethod{
CallingArgsLeadingComma: javaCallingArgStr(embedMethod.InArgs, true),
DeclarationArgs: javaDeclarationArgStr(embedMethod.InArgs, env, true),
IfaceName: vdlutil.FirstRuneToUpper(iface.Name),
Name: vdlutil.FirstRuneToLower(embedMethod.Name),
RetType: clientInterfaceRetType(iface, embedMethod, env),
}
}
// genJavaClientImplFile generates a client impl for the specified interface.
func genJavaClientImplFile(iface *compile.Interface, env *compile.Env) JavaFileInfo {
embeds := []clientImplEmbed{}
for _, embed := range allEmbeddedIfaces(iface) {
embeds = append(embeds, clientImplEmbed{
Name: vdlutil.FirstRuneToUpper(embed.Name),
FullName: javaPath(javaGenPkgPath(path.Join(embed.File.Package.GenPath, vdlutil.FirstRuneToUpper(embed.Name)))),
})
}
embedMethods := []clientImplEmbedMethod{}
for _, embedMao := range dedupedEmbeddedMethodAndOrigins(iface) {
embedMethods = append(embedMethods, processClientImplEmbedMethod(embedMao.Origin, embedMao.Method, env))
}
methods := make([]clientImplMethod, len(iface.Methods))
for i, method := range iface.Methods {
methods[i] = processClientImplMethod(iface, method, env)
}
javaServiceName := vdlutil.FirstRuneToUpper(iface.Name)
data := struct {
FileDoc string
EmbedMethods []clientImplEmbedMethod
Embeds []clientImplEmbed
FullServiceName string
Methods []clientImplMethod
PackagePath string
ServiceName string
Source string
}{
FileDoc: iface.File.Package.FileDoc,
EmbedMethods: embedMethods,
Embeds: embeds,
FullServiceName: javaPath(interfaceFullyQualifiedName(iface)),
Methods: methods,
PackagePath: javaPath(javaGenPkgPath(iface.File.Package.GenPath)),
ServiceName: javaServiceName,
Source: iface.File.BaseName,
}
var buf bytes.Buffer
err := parseTmpl("client impl", clientImplTmpl).Execute(&buf, data)
if err != nil {
log.Fatalf("vdl: couldn't execute client impl template: %v", err)
}
return JavaFileInfo{
Name: javaServiceName + "ClientImpl.java",
Data: buf.Bytes(),
}
}