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 }