blob: 688946365bebcc17298f1b1deb1fec0f8ea6647d [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"
"log"
"path"
"strings"
"v.io/x/ref/lib/vdl/compile"
"v.io/x/ref/lib/vdl/vdlutil"
)
const serverWrapperTmpl = header + `
// Source(s): {{ .Source }}
package {{ .PackagePath }};
/**
* Wrapper for {@link {{ .ServiceName }}Server}. This wrapper is used by
* {@link io.v.v23.rpc.ReflectInvoker} to indirectly invoke server methods.
*/
public final class {{ .ServiceName }}ServerWrapper {
private final {{ .FullServiceName }}Server server;
{{/* Define fields to hold each of the embedded server wrappers*/}}
{{ range $embed := .Embeds }}
{{/* e.g. private final com.somepackage.gen_impl.ArithStub stubArith; */}}
private final {{ $embed.FullName }}ServerWrapper wrapper{{ $embed.Name }};
{{ end }}
/**
* Creates a new {@link {{ .ServiceName }}ServerWrapper} to invoke the methods of the
* provided server.
*
* @param server server whose methods are to be invoked
*/
public {{ .ServiceName }}ServerWrapper({{ .FullServiceName }}Server server) {
this.server = server;
{{/* Initialize the embeded server wrappers */}}
{{ range $embed := .Embeds }}
this.wrapper{{ $embed.Name }} = new {{ $embed.FullName }}ServerWrapper(server);
{{ end }}
}
/**
* Returns a description of this server.
*/
public io.v.v23.vdlroot.signature.Interface signature() {
java.util.List<io.v.v23.vdlroot.signature.Embed> embeds = new java.util.ArrayList<io.v.v23.vdlroot.signature.Embed>();
java.util.List<io.v.v23.vdlroot.signature.Method> methods = new java.util.ArrayList<io.v.v23.vdlroot.signature.Method>();
{{ range $method := .Methods }}
{
java.util.List<io.v.v23.vdlroot.signature.Arg> inArgs = new java.util.ArrayList<io.v.v23.vdlroot.signature.Arg>();
{{ range $arg := $method.CallingArgTypes }}
inArgs.add(new io.v.v23.vdlroot.signature.Arg("", "", new io.v.v23.vdl.VdlTypeObject({{ $arg }})));
{{ end }}
java.util.List<io.v.v23.vdlroot.signature.Arg> outArgs = new java.util.ArrayList<io.v.v23.vdlroot.signature.Arg>();
{{ range $arg := $method.RetJavaTypes }}
outArgs.add(new io.v.v23.vdlroot.signature.Arg("", "", new io.v.v23.vdl.VdlTypeObject({{ $arg }})));
{{ end }}
java.util.List<io.v.v23.vdl.VdlAny> tags = new java.util.ArrayList<io.v.v23.vdl.VdlAny>();
{{ range $tag := .Tags }}
tags.add(new io.v.v23.vdl.VdlAny(io.v.v23.vdl.VdlValue.valueOf({{ $tag.Value }}, {{ $tag.Type }})));
{{ end }}
methods.add(new io.v.v23.vdlroot.signature.Method(
"{{ $method.Name }}",
"{{ $method.Doc }}",
inArgs,
outArgs,
null,
null,
tags));
}
{{ end }}
return new io.v.v23.vdlroot.signature.Interface("{{ .ServiceName }}", "{{ .PackagePath }}", "{{ .Doc }}", embeds, methods);
}
/**
* Returns all tags associated with the provided method or {@code null} if the method isn't
* implemented by this server.
*
* @param method method whose tags are to be returned
*/
@SuppressWarnings("unused")
public io.v.v23.vdl.VdlValue[] getMethodTags(java.lang.String method) throws io.v.v23.verror.VException {
{{ range $methodName, $tags := .MethodTags }}
if ("{{ $methodName }}".equals(method)) {
try {
return new io.v.v23.vdl.VdlValue[] {
{{ range $tag := $tags }} io.v.v23.vdl.VdlValue.valueOf({{ $tag.Value }}, {{ $tag.Type }}), {{ end }}
};
} catch (IllegalArgumentException e) {
throw new io.v.v23.verror.VException(String.format("Couldn't get tags for method \"{{ $methodName }}\": %s", e.getMessage()));
}
}
{{ end }}
{{ range $embed := .Embeds }}
{
io.v.v23.vdl.VdlValue[] tags = this.wrapper{{ $embed.Name }}.getMethodTags(method);
if (tags != null) {
return tags;
}
}
{{ end }}
return null; // method not found
}
{{/* Iterate over methods defined directly in the body of this server */}}
{{ range $method := .Methods }}
{{ $method.JavaDoc }}
public {{ $method.RetType }} {{ $method.Name }}(io.v.v23.context.VContext ctx, final io.v.v23.rpc.StreamServerCall call{{ $method.DeclarationArgs }}) throws io.v.v23.verror.VException {
{{ if $method.IsStreaming }}
io.v.v23.vdl.TypedStream<{{ $method.SendType }}, {{ $method.RecvType }}> _stream = new io.v.v23.vdl.TypedStream<{{ $method.SendType }}, {{ $method.RecvType }}>() {
@Override
public void send({{ $method.SendType }} item) throws io.v.v23.verror.VException {
java.lang.reflect.Type type = new com.google.common.reflect.TypeToken< {{ $method.SendType }} >() {}.getType();
call.send(item, type);
}
@Override
public {{ $method.RecvType }} recv() throws java.io.EOFException, io.v.v23.verror.VException {
java.lang.reflect.Type type = new com.google.common.reflect.TypeToken< {{ $method.RecvType }} >() {}.getType();
java.lang.Object result = call.recv(type);
try {
return ({{ $method.RecvType }})result;
} catch (java.lang.ClassCastException e) {
throw new io.v.v23.verror.VException("Unexpected result type: " + result.getClass().getCanonicalName());
}
}
};
{{ end }} {{/* end if $method.IsStreaming */}}
{{ if $method.Returns }} return {{ end }} this.server.{{ $method.Name }}(ctx, call {{ $method.CallingArgs }} {{ if $method.IsStreaming }} ,_stream {{ end }} );
}
{{end}}
{{/* Iterate over methods from embeded servers and generate code to delegate the work */}}
{{ range $eMethod := .EmbedMethods }}
{{ $eMethod.JavaDoc }}
public {{ $eMethod.RetType }} {{ $eMethod.Name }}(io.v.v23.context.VContext ctx, io.v.v23.rpc.StreamServerCall call{{ $eMethod.DeclarationArgs }}) throws io.v.v23.verror.VException {
{{/* e.g. return this.stubArith.cosine(ctx, call, [args], options) */}}
{{ if $eMethod.Returns }}return{{ end }} this.wrapper{{ $eMethod.IfaceName }}.{{ $eMethod.Name }}(ctx, call{{ $eMethod.CallingArgs }});
}
{{ end }} {{/* end range .EmbedMethods */}}
}
`
type serverWrapperMethod struct {
CallingArgs string
CallingArgTypes []string
DeclarationArgs string
Doc string
IsStreaming bool
JavaDoc string
Name string
RecvType string
RetType string
RetJavaTypes []string
Returns bool
SendType string
Tags []methodTag
}
type serverWrapperEmbedMethod struct {
CallingArgs string
DeclarationArgs string
Doc string
IfaceName string
JavaDoc string
Name string
RetType string
Returns bool
}
type serverWrapperEmbed struct {
Name string
FullName string
}
type methodTag struct {
Value string
Type string
}
// TODO(sjr): move this to somewhere in util_*.
func toJavaString(goString string) string {
result := strings.Replace(goString, "\"", "\\\"", -1)
result = strings.Replace(result, "\n", "\" + \n\"", -1)
return result
}
func processServerWrapperMethod(iface *compile.Interface, method *compile.Method, env *compile.Env, tags []methodTag) serverWrapperMethod {
callArgTypes := make([]string, len(method.InArgs))
for i, arg := range method.InArgs {
callArgTypes[i] = javaReflectType(arg.Type, env)
}
retArgTypes := make([]string, len(method.OutArgs))
for i, arg := range method.OutArgs {
retArgTypes[i] = javaReflectType(arg.Type, env)
}
return serverWrapperMethod{
CallingArgs: javaCallingArgStr(method.InArgs, true),
CallingArgTypes: callArgTypes,
DeclarationArgs: javaDeclarationArgStr(method.InArgs, env, true),
Doc: toJavaString(method.Doc),
IsStreaming: isStreamingMethod(method),
JavaDoc: javaDoc(method.Doc, method.DocSuffix),
Name: vdlutil.FirstRuneToLower(method.Name),
RecvType: javaType(method.InStream, true, env),
RetType: serverInterfaceOutArg(iface, method, env),
RetJavaTypes: retArgTypes,
Returns: len(method.OutArgs) >= 1,
SendType: javaType(method.OutStream, true, env),
Tags: tags,
}
}
func processServerWrapperEmbedMethod(iface *compile.Interface, embedMethod *compile.Method, env *compile.Env) serverWrapperEmbedMethod {
return serverWrapperEmbedMethod{
CallingArgs: javaCallingArgStr(embedMethod.InArgs, true),
DeclarationArgs: javaDeclarationArgStr(embedMethod.InArgs, env, true),
IfaceName: vdlutil.FirstRuneToUpper(iface.Name),
JavaDoc: javaDoc(embedMethod.Doc, embedMethod.DocSuffix),
Name: vdlutil.FirstRuneToLower(embedMethod.Name),
RetType: serverInterfaceOutArg(iface, embedMethod, env),
Returns: len(embedMethod.OutArgs) >= 1,
}
}
// genJavaServerWrapperFile generates a java file containing a server wrapper for the specified
// interface.
func genJavaServerWrapperFile(iface *compile.Interface, env *compile.Env) JavaFileInfo {
embeds := []serverWrapperEmbed{}
for _, embed := range allEmbeddedIfaces(iface) {
embeds = append(embeds, serverWrapperEmbed{
Name: vdlutil.FirstRuneToUpper(embed.Name),
FullName: javaPath(javaGenPkgPath(path.Join(embed.File.Package.GenPath, vdlutil.FirstRuneToUpper(embed.Name)))),
})
}
methodTags := make(map[string][]methodTag)
// Copy method tags off of the interface.
methods := make([]serverWrapperMethod, len(iface.Methods))
for i, method := range iface.Methods {
tags := make([]methodTag, len(method.Tags))
for j, tag := range method.Tags {
tags[j].Value = javaConstVal(tag, env)
tags[j].Type = javaReflectType(tag.Type(), env)
}
methodTags[vdlutil.FirstRuneToLower(method.Name)] = tags
methods[i] = processServerWrapperMethod(iface, method, env, tags)
}
embedMethods := []serverWrapperEmbedMethod{}
for _, embedMao := range dedupedEmbeddedMethodAndOrigins(iface) {
embedMethods = append(embedMethods, processServerWrapperEmbedMethod(embedMao.Origin, embedMao.Method, env))
}
javaServiceName := vdlutil.FirstRuneToUpper(iface.Name)
data := struct {
FileDoc string
EmbedMethods []serverWrapperEmbedMethod
Embeds []serverWrapperEmbed
FullServiceName string
Methods []serverWrapperMethod
MethodTags map[string][]methodTag
PackagePath string
ServiceName string
Source string
Doc string
}{
FileDoc: iface.File.Package.FileDoc,
EmbedMethods: embedMethods,
Embeds: embeds,
FullServiceName: javaPath(interfaceFullyQualifiedName(iface)),
Methods: methods,
MethodTags: methodTags,
PackagePath: javaPath(javaGenPkgPath(iface.File.Package.GenPath)),
ServiceName: javaServiceName,
Source: iface.File.BaseName,
Doc: toJavaString(iface.NamePos.Doc),
}
var buf bytes.Buffer
err := parseTmpl("server wrapper", serverWrapperTmpl).Execute(&buf, data)
if err != nil {
log.Fatalf("vdl: couldn't execute server wrapper template: %v", err)
}
return JavaFileInfo{
Name: javaServiceName + "ServerWrapper.java",
Data: buf.Bytes(),
}
}