dubbo.apache.org/dubbo-go/v3@v3.1.1/protocol/grpc/protoc-gen-dubbo/plugin/dubbo/dubbo.go (about)

     1  /*
     2   * Licensed to the Apache Software Foundation (ASF) under one or more
     3   * contributor license agreements.  See the NOTICE file distributed with
     4   * this work for additional information regarding copyright ownership.
     5   * The ASF licenses this file to You under the Apache License, Version 2.0
     6   * (the "License"); you may not use this file except in compliance with
     7   * the License.  You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  package dubbo
    19  
    20  import (
    21  	"fmt"
    22  	"strconv"
    23  	"strings"
    24  )
    25  
    26  import (
    27  	pb "github.com/golang/protobuf/protoc-gen-go/descriptor"
    28  	"github.com/golang/protobuf/protoc-gen-go/generator"
    29  )
    30  
    31  // Paths for packages used by code generated in this file,
    32  // relative to the import_prefix of the generator.Generator.
    33  const (
    34  	contextPkgPath = "context"
    35  	grpcPkgPath    = "google.golang.org/grpc"
    36  )
    37  
    38  func init() {
    39  	generator.RegisterPlugin(new(dubboGrpc))
    40  }
    41  
    42  // grpc is an implementation of the Go protocol buffer compiler's
    43  // plugin architecture.  It generates bindings for gRPC-dubbo support.
    44  type dubboGrpc struct {
    45  	gen *generator.Generator
    46  }
    47  
    48  // Name returns the name of this plugin, "grpc".
    49  func (g *dubboGrpc) Name() string {
    50  	return "dubbo"
    51  }
    52  
    53  // The names for packages imported in the generated code.
    54  // They may vary from the final path component of the import path
    55  // if the name is used by other packages.
    56  var (
    57  	contextPkg string
    58  	grpcPkg    string
    59  )
    60  
    61  // Init initializes the plugin.
    62  func (g *dubboGrpc) Init(gen *generator.Generator) {
    63  	g.gen = gen
    64  }
    65  
    66  // Given a type name defined in a .proto, return its object.
    67  // Also record that we're using it, to guarantee the associated import.
    68  func (g *dubboGrpc) objectNamed(name string) generator.Object {
    69  	g.gen.RecordTypeUse(name)
    70  	return g.gen.ObjectNamed(name)
    71  }
    72  
    73  // Given a type name defined in a .proto, return its name as we will print it.
    74  func (g *dubboGrpc) typeName(str string) string {
    75  	return g.gen.TypeName(g.objectNamed(str))
    76  }
    77  
    78  // P forwards to g.gen.P.
    79  func (g *dubboGrpc) P(args ...interface{}) { g.gen.P(args...) }
    80  
    81  // Generate generates code for the services in the given file.
    82  // be consistent with grpc plugin
    83  func (g *dubboGrpc) Generate(file *generator.FileDescriptor) {
    84  	if len(file.FileDescriptorProto.Service) == 0 {
    85  		return
    86  	}
    87  
    88  	contextPkg = string(g.gen.AddImport(contextPkgPath))
    89  	grpcPkg = string(g.gen.AddImport(grpcPkgPath))
    90  
    91  	for i, service := range file.FileDescriptorProto.Service {
    92  		g.generateService(file, service, i)
    93  	}
    94  }
    95  
    96  // GenerateImports generates the import declaration for this file.
    97  func (g *dubboGrpc) GenerateImports(file *generator.FileDescriptor) {
    98  	g.P("import (")
    99  	g.P(`"dubbo.apache.org/dubbo-go/v3/protocol/invocation"`)
   100  	g.P(`"dubbo.apache.org/dubbo-go/v3/protocol"`)
   101  	g.P(` ) `)
   102  }
   103  
   104  func unexport(s string) string { return strings.ToLower(s[:1]) + s[1:] }
   105  
   106  // deprecationComment is the standard comment added to deprecated
   107  // messages, fields, enums, and enum values.
   108  var deprecationComment = "// Deprecated: Do not use."
   109  
   110  // generateService generates all the code for the named service.
   111  func (g *dubboGrpc) generateService(file *generator.FileDescriptor, service *pb.ServiceDescriptorProto, index int) {
   112  	path := fmt.Sprintf("6,%d", index) // 6 means service.
   113  
   114  	origServName := service.GetName()
   115  	fullServName := origServName
   116  	if pkg := file.GetPackage(); pkg != "" {
   117  		fullServName = pkg + "." + fullServName
   118  	}
   119  	servName := generator.CamelCase(origServName)
   120  	deprecated := service.GetOptions().GetDeprecated()
   121  
   122  	g.P()
   123  	g.P(fmt.Sprintf(`// %sClientImpl is the client API for %s service.
   124  //
   125  // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.`, servName, servName))
   126  
   127  	// Client interface.
   128  	if deprecated {
   129  		g.P("//")
   130  		g.P(deprecationComment)
   131  	}
   132  	dubboSrvName := servName + "ClientImpl"
   133  	g.P("type ", dubboSrvName, " struct {")
   134  	for i, method := range service.Method {
   135  		g.gen.PrintComments(fmt.Sprintf("%s,2,%d", path, i)) // 2 means method in a service.
   136  		if method.GetOptions().GetDeprecated() {
   137  			g.P("//")
   138  			g.P(deprecationComment)
   139  		}
   140  		g.P(g.generateClientSignature(servName, method))
   141  	}
   142  	g.P("}")
   143  	g.P()
   144  
   145  	// NewClient factory.
   146  	if deprecated {
   147  		g.P(deprecationComment)
   148  	}
   149  
   150  	// add Reference method
   151  	//func (u *GrpcGreeterImpl) Reference() string {
   152  	//	return "GrpcGreeterImpl"
   153  	//}
   154  	g.P("func (c *", dubboSrvName, ") ", " Reference() string ", "{")
   155  	g.P(`return "`, unexport(servName), `Impl"`)
   156  	g.P("}")
   157  	g.P()
   158  
   159  	// add GetDubboStub method
   160  	// func (u *GrpcGreeterImpl) GetDubboStub(cc *grpc.ClientConn) GreeterClient {
   161  	//	return NewGreeterClient(cc)
   162  	//}
   163  	g.P("func (c *", dubboSrvName, ") ", " GetDubboStub(cc *grpc.ClientConn) ", servName, "Client {")
   164  	g.P(`return New`, servName, `Client(cc)`)
   165  	g.P("}")
   166  	g.P()
   167  
   168  	// Server interface.
   169  	serverType := servName + "ProviderBase"
   170  	g.P("type ", serverType, " struct {")
   171  	g.P("proxyImpl protocol.Invoker")
   172  	g.P("}")
   173  	g.P()
   174  
   175  	// add set method
   176  	//func (g *GreeterProviderBase) SetProxyImpl(impl protocol.Invoker) {
   177  	//	g.proxyImpl = impl
   178  	//}
   179  	g.P("func (s *", serverType, ") SetProxyImpl(impl protocol.Invoker) {")
   180  	g.P(`s.proxyImpl = impl`)
   181  	g.P("}")
   182  	g.P()
   183  
   184  	// return get method
   185  	g.P("func (s *", serverType, ") GetProxyImpl() protocol.Invoker {")
   186  	g.P(`return s.proxyImpl`)
   187  	g.P("}")
   188  	g.P()
   189  
   190  	// return reference
   191  	g.P("func (c *", serverType, ") ", " Reference() string ", "{")
   192  	g.P(`return "`, unexport(servName), `Impl"`)
   193  	g.P("}")
   194  	g.P()
   195  
   196  	// add handler
   197  	var handlerNames []string
   198  	for _, method := range service.Method {
   199  		hname := g.generateServerMethod(servName, fullServName, method)
   200  		handlerNames = append(handlerNames, hname)
   201  	}
   202  
   203  	grpcserverType := servName + "Server"
   204  	// return service desc
   205  	g.P("func (s *", serverType, ") ServiceDesc() *grpc.ServiceDesc {")
   206  	g.P(`return &grpc.ServiceDesc{`)
   207  	g.P("ServiceName: ", strconv.Quote(fullServName), ",")
   208  	g.P("HandlerType: (*", grpcserverType, ")(nil),")
   209  	g.P("Methods: []", grpcPkg, ".MethodDesc{")
   210  	for i, method := range service.Method {
   211  		if method.GetServerStreaming() || method.GetClientStreaming() {
   212  			continue
   213  		}
   214  		g.P("{")
   215  		g.P("MethodName: ", strconv.Quote(method.GetName()), ",")
   216  		g.P("Handler: ", handlerNames[i], ",")
   217  		g.P("},")
   218  	}
   219  	g.P("},")
   220  	g.P("Streams: []", grpcPkg, ".StreamDesc{")
   221  	for i, method := range service.Method {
   222  		if !method.GetClientStreaming() && !method.GetServerStreaming() {
   223  			continue
   224  		}
   225  		g.P("{")
   226  		g.P("StreamName: ", strconv.Quote(method.GetName()), ",")
   227  		g.P("Handler: ", handlerNames[i], ",")
   228  		if method.GetServerStreaming() {
   229  			g.P("ServerStreams: true,")
   230  		}
   231  		if method.GetClientStreaming() {
   232  			g.P("ClientStreams: true,")
   233  		}
   234  		g.P("},")
   235  	}
   236  	g.P("},")
   237  	g.P("Metadata: \"", file.GetName(), "\",")
   238  	g.P("}")
   239  	g.P("}")
   240  	g.P()
   241  }
   242  
   243  // generateClientSignature returns the client-side signature for a method.
   244  func (g *dubboGrpc) generateClientSignature(servName string, method *pb.MethodDescriptorProto) string {
   245  	origMethName := method.GetName()
   246  	methName := generator.CamelCase(origMethName)
   247  	//if reservedClientName[methName] {
   248  	//  methName += "_"
   249  	//}
   250  	reqArg := ", in *" + g.typeName(method.GetInputType())
   251  	if method.GetClientStreaming() {
   252  		reqArg = ""
   253  	}
   254  	respName := "out *" + g.typeName(method.GetOutputType())
   255  	if method.GetServerStreaming() || method.GetClientStreaming() {
   256  		respName = servName + "_" + generator.CamelCase(origMethName) + "Client"
   257  		return fmt.Sprintf("%s func(ctx %s.Context%s) (%s, error)", methName, contextPkg, reqArg, respName)
   258  	}
   259  	return fmt.Sprintf("%s func(ctx %s.Context%s, %s) error", methName, contextPkg, reqArg, respName)
   260  }
   261  
   262  func (g *dubboGrpc) generateServerMethod(servName, fullServName string, method *pb.MethodDescriptorProto) string {
   263  	methName := generator.CamelCase(method.GetName())
   264  	hname := fmt.Sprintf("_DUBBO_%s_%s_Handler", servName, methName)
   265  	inType := g.typeName(method.GetInputType())
   266  
   267  	if !method.GetServerStreaming() && !method.GetClientStreaming() {
   268  		g.P("func ", hname, "(srv interface{}, ctx ", contextPkg, ".Context, dec func(interface{}) error, interceptor ", grpcPkg, ".UnaryServerInterceptor) (interface{}, error) {")
   269  		g.P("in := new(", inType, ")")
   270  		g.P("if err := dec(in); err != nil { return nil, err }")
   271  		g.P(`// DubboGrpcService is gRPC service
   272  type DubboGrpcService interface {
   273  	// SetProxyImpl sets proxy.
   274  	SetProxyImpl(impl protocol.Invoker)
   275  	// GetProxyImpl gets proxy.
   276  	GetProxyImpl() protocol.Invoker
   277  	// ServiceDesc gets an RPC service's specification.
   278  	ServiceDesc() *grpc.ServiceDesc
   279  }`)
   280  		g.P("base := srv.(DubboGrpcService)")
   281  		g.P("args := []interface{}{}")
   282  		g.P("args = append(args, in)")
   283  		g.P(`invo := invocation.NewRPCInvocation("`, methName, `", args, nil)`)
   284  
   285  		g.P("if interceptor == nil {")
   286  		g.P("result := base.GetProxyImpl().Invoke(ctx, invo)")
   287  		g.P("return result.Result(), result.Error()")
   288  		g.P("}")
   289  
   290  		g.P("info := &", grpcPkg, ".UnaryServerInfo{")
   291  		g.P("Server: srv,")
   292  		g.P("FullMethod: ", strconv.Quote(fmt.Sprintf("/%s/%s", fullServName, methName)), ",")
   293  		g.P("}")
   294  
   295  		g.P("handler := func(ctx ", contextPkg, ".Context, req interface{}) (interface{}, error) {")
   296  		g.P("result := base.GetProxyImpl().Invoke(ctx, invo)")
   297  		g.P("return result.Result(), result.Error()")
   298  		g.P("}")
   299  
   300  		g.P("return interceptor(ctx, in, info, handler)")
   301  		g.P("}")
   302  		g.P()
   303  		return hname
   304  	}
   305  	streamType := unexport(servName) + methName + "Server"
   306  	g.P("func ", hname, "(srv interface{}, stream ", grpcPkg, ".ServerStream) error {")
   307  	g.P(`// DubboGrpcService is gRPC service
   308  type DubboGrpcService interface {
   309  	// SetProxyImpl sets proxy.
   310  	SetProxyImpl(impl protocol.Invoker)
   311  	// GetProxyImpl gets proxy.
   312  	GetProxyImpl() protocol.Invoker
   313  	// ServiceDesc gets an RPC service's specification.
   314  	ServiceDesc() *grpc.ServiceDesc
   315  }`)
   316  	g.P("_, ok := srv.(DubboGrpcService)")
   317  	g.P(`invo := invocation.NewRPCInvocation("`, methName, `", nil, nil)`)
   318  	g.P("if !ok {")
   319  	g.P("fmt.Println(invo)")
   320  	g.P("}")
   321  	if !method.GetClientStreaming() {
   322  		g.P("m := new(", inType, ")")
   323  		g.P("if err := stream.RecvMsg(m); err != nil { return err }")
   324  		g.P("return srv.(", servName, "Server).", methName, "(m, &", streamType, "{stream})")
   325  	} else {
   326  		g.P("return srv.(", servName, "Server).", methName, "(&", streamType, "{stream})")
   327  	}
   328  	g.P("}")
   329  	g.P()
   330  
   331  	return hname
   332  }