| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package proxy_factory |
| |
| import ( |
| "context" |
| "reflect" |
| "strings" |
| ) |
| |
| import ( |
| perrors "github.com/pkg/errors" |
| ) |
| |
| import ( |
| "github.com/apache/dubbo-go/common" |
| "github.com/apache/dubbo-go/common/constant" |
| "github.com/apache/dubbo-go/common/extension" |
| "github.com/apache/dubbo-go/common/logger" |
| "github.com/apache/dubbo-go/common/proxy" |
| "github.com/apache/dubbo-go/protocol" |
| ) |
| |
| func init() { |
| extension.SetProxyFactory("default", NewDefaultProxyFactory) |
| } |
| |
| // DefaultProxyFactory is the default proxy factory |
| type DefaultProxyFactory struct { // delegate ProxyFactory |
| } |
| |
| // you can rewrite DefaultProxyFactory in extension and delegate the default proxy factory like below |
| |
| //func WithDelegate(delegateProxyFactory ProxyFactory) Option { |
| // return func(proxy ProxyFactory) { |
| // proxy.(*DefaultProxyFactory).delegate = delegateProxyFactory |
| // } |
| //} |
| |
| // NewDefaultProxyFactory returns a proxy factory instance |
| func NewDefaultProxyFactory(_ ...proxy.Option) proxy.ProxyFactory { |
| return &DefaultProxyFactory{} |
| } |
| |
| // GetProxy gets a proxy |
| func (factory *DefaultProxyFactory) GetProxy(invoker protocol.Invoker, url *common.URL) *proxy.Proxy { |
| return factory.GetAsyncProxy(invoker, nil, url) |
| } |
| |
| // GetAsyncProxy gets a async proxy |
| func (factory *DefaultProxyFactory) GetAsyncProxy(invoker protocol.Invoker, callBack interface{}, url *common.URL) *proxy.Proxy { |
| // create proxy |
| attachments := map[string]string{} |
| attachments[constant.ASYNC_KEY] = url.GetParam(constant.ASYNC_KEY, "false") |
| return proxy.NewProxy(invoker, callBack, attachments) |
| } |
| |
| // GetInvoker gets a invoker |
| func (factory *DefaultProxyFactory) GetInvoker(url *common.URL) protocol.Invoker { |
| return &ProxyInvoker{ |
| BaseInvoker: *protocol.NewBaseInvoker(url), |
| } |
| } |
| |
| // ProxyInvoker is a invoker struct |
| type ProxyInvoker struct { |
| protocol.BaseInvoker |
| } |
| |
| // Invoke is used to call service method by invocation |
| func (pi *ProxyInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { |
| result := &protocol.RPCResult{} |
| result.SetAttachments(invocation.Attachments()) |
| |
| // get providerUrl. The origin url may be is registry URL. |
| url := getProviderURL(pi.GetUrl()) |
| |
| methodName := invocation.MethodName() |
| proto := url.Protocol |
| path := strings.TrimPrefix(url.Path, "/") |
| args := invocation.Arguments() |
| |
| // get service |
| svc := common.ServiceMap.GetServiceByServiceKey(proto, url.ServiceKey()) |
| if svc == nil { |
| logger.Errorf("cannot find service [%s] in %s", path, proto) |
| result.SetError(perrors.Errorf("cannot find service [%s] in %s", path, proto)) |
| return result |
| } |
| |
| // get method |
| method := svc.Method()[methodName] |
| if method == nil { |
| logger.Errorf("cannot find method [%s] of service [%s] in %s", methodName, path, proto) |
| result.SetError(perrors.Errorf("cannot find method [%s] of service [%s] in %s", methodName, path, proto)) |
| return result |
| } |
| |
| in := []reflect.Value{svc.Rcvr()} |
| if method.CtxType() != nil { |
| ctx = context.WithValue(ctx, constant.AttachmentKey, invocation.Attachments()) |
| in = append(in, method.SuiteContext(ctx)) |
| } |
| |
| // prepare argv |
| if (len(method.ArgsType()) == 1 || len(method.ArgsType()) == 2 && method.ReplyType() == nil) && method.ArgsType()[0].String() == "[]interface {}" { |
| in = append(in, reflect.ValueOf(args)) |
| } else { |
| for i := 0; i < len(args); i++ { |
| t := reflect.ValueOf(args[i]) |
| if !t.IsValid() { |
| at := method.ArgsType()[i] |
| if at.Kind() == reflect.Ptr { |
| at = at.Elem() |
| } |
| t = reflect.New(at) |
| } |
| in = append(in, t) |
| } |
| } |
| |
| // prepare replyv |
| var replyv reflect.Value |
| if method.ReplyType() == nil && len(method.ArgsType()) > 0 { |
| replyv = reflect.New(method.ArgsType()[len(method.ArgsType())-1].Elem()) |
| in = append(in, replyv) |
| } |
| |
| returnValues := method.Method().Func.Call(in) |
| |
| var retErr interface{} |
| if len(returnValues) == 1 { |
| retErr = returnValues[0].Interface() |
| } else { |
| replyv = returnValues[0] |
| retErr = returnValues[1].Interface() |
| } |
| if retErr != nil { |
| result.SetError(retErr.(error)) |
| } else { |
| if replyv.IsValid() && (replyv.Kind() != reflect.Ptr || replyv.Kind() == reflect.Ptr && replyv.Elem().IsValid()) { |
| result.SetResult(replyv.Interface()) |
| } |
| } |
| return result |
| } |
| |
| func getProviderURL(url *common.URL) *common.URL { |
| if url.SubURL == nil { |
| return url |
| } |
| return url.SubURL |
| } |