func (*reflectSuite) TestTypeOf(c *gc.C) { rtype := rpcreflect.TypeOf(reflect.TypeOf(&Root{})) c.Assert(rtype.DiscardedMethods(), gc.DeepEquals, []string{ "Discard1", "Discard2", "Discard3", }) expect := map[string]reflect.Type{ "CallbackMethods": reflect.TypeOf(&CallbackMethods{}), "ChangeAPIMethods": reflect.TypeOf(&ChangeAPIMethods{}), "DelayedMethods": reflect.TypeOf(&DelayedMethods{}), "ErrorMethods": reflect.TypeOf(&ErrorMethods{}), "InterfaceMethods": reflect.TypeOf((*InterfaceMethods)(nil)).Elem(), "SimpleMethods": reflect.TypeOf(&SimpleMethods{}), } c.Assert(rtype.MethodNames(), gc.HasLen, len(expect)) for name, expectGoType := range expect { m, err := rtype.Method(name) c.Assert(err, jc.ErrorIsNil) c.Assert(m, gc.NotNil) c.Assert(m.Call, gc.NotNil) c.Assert(m.ObjType, gc.Equals, rpcreflect.ObjTypeOf(expectGoType)) c.Assert(m.ObjType.GoType(), gc.Equals, expectGoType) } m, err := rtype.Method("not found") c.Assert(err, gc.Equals, rpcreflect.ErrMethodNotFound) c.Assert(m, gc.DeepEquals, rpcreflect.RootMethod{}) }
func (r *apiRoot) lookupMethod(rootName string, version int, methodName string) (reflect.Type, rpcreflect.ObjMethod, error) { noMethod := rpcreflect.ObjMethod{} goType, err := common.Facades.GetType(rootName, version) if err != nil { if errors.IsNotFound(err) { return nil, noMethod, &rpcreflect.CallNotImplementedError{ RootMethod: rootName, Version: version, } } return nil, noMethod, err } rpcType := rpcreflect.ObjTypeOf(goType) objMethod, err := rpcType.Method(methodName) if err != nil { if err == rpcreflect.ErrMethodNotFound { return nil, noMethod, &rpcreflect.CallNotImplementedError{ RootMethod: rootName, Version: version, Method: methodName, } } return nil, noMethod, err } return goType, objMethod, nil }
func (cc *CustomMethodFinder) FindMethod( rootMethodName string, version int, objMethodName string, ) ( rpcreflect.MethodCaller, error, ) { logger.Debugf("got to FindMethod: %q %d %q", rootMethodName, version, objMethodName) if rootMethodName != "MultiVersion" { return nil, &rpcreflect.CallNotImplementedError{ RootMethod: rootMethodName, } } var goType reflect.Type var wrap wrapper switch version { case 0: goType = reflect.TypeOf((*VariableMethods1)(nil)) wrap = func(sm *SimpleMethods) reflect.Value { return reflect.ValueOf(&VariableMethods1{sm}) } case 1: goType = reflect.TypeOf((*VariableMethods2)(nil)) wrap = func(sm *SimpleMethods) reflect.Value { return reflect.ValueOf(&VariableMethods2{sm}) } case 2: goType = reflect.TypeOf((*RestrictedMethods)(nil)) wrap = func(sm *SimpleMethods) reflect.Value { methods := &RestrictedMethods{InterfaceMethods: sm} return reflect.ValueOf(methods) } default: return nil, &rpcreflect.CallNotImplementedError{ RootMethod: rootMethodName, Version: version, } } logger.Debugf("found type: %s", goType) objType := rpcreflect.ObjTypeOf(goType) objMethod, err := objType.Method(objMethodName) if err != nil { return nil, &rpcreflect.CallNotImplementedError{ RootMethod: rootMethodName, Version: version, Method: objMethodName, } } return customMethodCaller{ objMethod: objMethod, root: cc.root, wrap: wrap, expectedType: goType, }, nil }
func (*facadeRegistrySuite) TestDiscardedAPIMethods(c *gc.C) { allFacades := common.Facades.List() c.Assert(allFacades, gc.Not(gc.HasLen), 0) for _, description := range allFacades { for _, version := range description.Versions { facadeType, err := common.Facades.GetType(description.Name, version) c.Assert(err, gc.IsNil) facadeObjType := rpcreflect.ObjTypeOf(facadeType) // We must have some methods on every object returned // by a root-level method. c.Assert(facadeObjType.MethodNames(), gc.Not(gc.HasLen), 0) // We don't allow any methods that don't implement // an RPC entry point. c.Assert(facadeObjType.DiscardedMethods(), gc.HasLen, 0) } } }
func (*reflectSuite) TestObjTypeOf(c *gc.C) { objType := rpcreflect.ObjTypeOf(reflect.TypeOf(&SimpleMethods{})) c.Check(objType.DiscardedMethods(), gc.DeepEquals, []string{ "Discard1", "Discard2", "Discard3", "Discard4", }) expect := map[string]*rpcreflect.ObjMethod{ "SliceArg": { Params: reflect.TypeOf(struct{ X []string }{}), Result: reflect.TypeOf(stringVal{}), }, } for narg := 0; narg < 2; narg++ { for nret := 0; nret < 2; nret++ { for nerr := 0; nerr < 2; nerr++ { retErr := nerr != 0 var m rpcreflect.ObjMethod if narg > 0 { m.Params = reflect.TypeOf(stringVal{}) } if nret > 0 { m.Result = reflect.TypeOf(stringVal{}) } expect[callName(narg, nret, retErr)] = &m } } } c.Assert(objType.MethodNames(), gc.HasLen, len(expect)) for name, expectMethod := range expect { m, err := objType.Method(name) c.Check(err, jc.ErrorIsNil) c.Assert(m, gc.NotNil) c.Check(m.Call, gc.NotNil) c.Check(m.Params, gc.Equals, expectMethod.Params) c.Check(m.Result, gc.Equals, expectMethod.Result) } m, err := objType.Method("not found") c.Check(err, gc.Equals, rpcreflect.ErrMethodNotFound) c.Check(m, gc.DeepEquals, rpcreflect.ObjMethod{}) }
func generateInfo() (*apidoc.Info, error) { serverPkg := "github.com/juju/juju/apiserver" cfg := loader.Config{ TypeCheckFuncBodies: func(string) bool { return true }, ImportPkgs: map[string]bool{ serverPkg: false, // false means don't load tests. }, ParserMode: parser.ParseComments, } prog, err := cfg.Load() if err != nil { return nil, errgo.Notef(err, "cannot load %q", serverPkg) } info := jsontypes.NewInfo() ds := common.Facades.ListDetails() ds = append(ds, apiserver.AdminFacadeDetails()...) for _, d := range ds { t := rpcreflect.ObjTypeOf(d.Type) for _, name := range t.MethodNames() { m, _ := t.Method(name) if m.Params != nil { info.TypeInfo(m.Params) } if m.Result != nil { info.TypeInfo(m.Result) } } } apiInfo := &apidoc.Info{ TypeInfo: info, } for _, d := range ds { f := apidoc.FacadeInfo{ Name: d.Name, Version: d.Version, } pt, err := progType(prog, d.Type) if err != nil { return nil, errgo.Notef(err, "cannot get prog type for %v", d.Type) } tdoc, err := typeDocComment(prog, pt) if err != nil { return nil, errgo.Notef(err, "cannot get doc comment for %v: %v", d.Type) } f.Doc = tdoc t := rpcreflect.ObjTypeOf(d.Type) for _, name := range t.MethodNames() { m, _ := t.Method(name) fm := apidoc.Method{ Name: name, } if m.Params != nil { fm.Param = info.Ref(m.Params) } if m.Result != nil { fm.Result = info.Ref(m.Result) } mdoc, err := methodDocComment(prog, pt, name) if err != nil { return nil, errgo.Notef(err, "cannot get doc comment for %v.%v: %v", d.Type, name) } fm.Doc = mdoc f.Methods = append(f.Methods, fm) } apiInfo.Facades = append(apiInfo.Facades, f) } return apiInfo, nil }
// FindMethod looks up the given rootName and version in our facade registry // and returns a MethodCaller that will be used by the RPC code to place calls on // that facade. // FindMethod uses the global registry state/apiserver/common.Facades. // For more information about how FindMethod should work, see rpc/server.go and // rpc/rpcreflect/value.go func (r *srvRoot) FindMethod(rootName string, version int, methodName string) (rpcreflect.MethodCaller, error) { goType, err := common.Facades.GetType(rootName, version) if err != nil { if errors.IsNotFound(err) { return nil, &rpcreflect.CallNotImplementedError{ RootMethod: rootName, Version: version, } } return nil, err } rpcType := rpcreflect.ObjTypeOf(goType) objMethod, err := rpcType.Method(methodName) if err != nil { if err == rpcreflect.ErrMethodNotFound { return nil, &rpcreflect.CallNotImplementedError{ RootMethod: rootName, Version: version, Method: methodName, } } return nil, err } creator := func(id string) (reflect.Value, error) { objKey := objectKey{name: rootName, version: version, objId: id} r.objectMutex.RLock() objValue, ok := r.objectCache[objKey] r.objectMutex.RUnlock() if ok { return objValue, nil } r.objectMutex.Lock() defer r.objectMutex.Unlock() if objValue, ok := r.objectCache[objKey]; ok { return objValue, nil } // Now that we have the write lock, check one more time in case // someone got the write lock before us. factory, err := common.Facades.GetFactory(rootName, version) if err != nil { // We don't check for IsNotFound here, because it // should have already been handled in the GetType // check. return reflect.Value{}, err } obj, err := factory(r.state, r.resources, r, id) if err != nil { return reflect.Value{}, err } objValue = reflect.ValueOf(obj) if !objValue.Type().AssignableTo(goType) { return reflect.Value{}, errors.Errorf( "internal error, %s(%d) claimed to return %s but returned %T", rootName, version, goType, obj) } if goType.Kind() == reflect.Interface { // If the original function wanted to return an // interface type, the indirection in the factory via // an interface{} strips the original interface // information off. So here we have to create the // interface again, and assign it. asInterface := reflect.New(goType).Elem() asInterface.Set(objValue) objValue = asInterface } r.objectCache[objKey] = objValue return objValue, nil } return &srvCaller{ creator: creator, objMethod: objMethod, }, nil }