package registry import ( "fmt" "reflect" "strconv" cuej "git.loafle.net/commons/util-go/encoding/json" cus "git.loafle.net/commons/util-go/service" ) // NewRESTRegistry returns a new REST registry. func NewRESTRegistry() RESTRegistry { return &restRegistry{ serviceRegistry: new(cus.ServiceRegistry), } } type RESTRegistry interface { RESTInvoker GetService(name string) interface{} RegisterService(receiver interface{}, name string) error RegisterServices(receivers ...interface{}) error RegisterServiceMap(keysAndValues map[string]interface{}) error } // RESTRegistry serves registered REST services using registered codecs. type restRegistry struct { serviceRegistry *cus.ServiceRegistry } // RegisterService adds a new service to the server. // // The name parameter is optional: if empty it will be inferred from // the receiver type name. // // Methods from the receiver will be extracted if these rules are satisfied: // // - The receiver is exported (begins with an upper case letter) or local // (defined in the package registering the service). // - The method name is exported. // - The method has two arguments: *args, *reply. // - All two arguments are pointers. // - The first and second arguments are exported or local. // - The method has return type error. // // All other methods are ignored. func (rr *restRegistry) RegisterService(receiver interface{}, name string) error { return rr.serviceRegistry.Register(receiver, name) } func (rr *restRegistry) RegisterServices(receivers ...interface{}) error { if nil == receivers || 0 == len(receivers) { return nil } for _, receiver := range receivers { if err := rr.serviceRegistry.Register(receiver, ""); nil != err { return err } } return nil } func (rr *restRegistry) RegisterServiceMap(keysAndValues map[string]interface{}) error { if nil == keysAndValues || 0 == len(keysAndValues) { return nil } for name, receiver := range keysAndValues { if err := rr.serviceRegistry.Register(receiver, name); nil != err { return err } } return nil } func (rr *restRegistry) GetService(name string) interface{} { return rr.serviceRegistry.GetService(name) } // HasMethod returns true if the given method is registered. // // The method uses a dotted notation as in "Service.Method". func (rr *restRegistry) HasMethod(method string) bool { if _, _, err := rr.serviceRegistry.Get(method); err == nil { return true } return false } // Invoke execute a method. // // Codecs are defined to process a given serialization scheme, e.g., JSON or // XML. A codec is chosen based on the "Content-Type" header from the request, // excluding the charset definition. func (rr *restRegistry) InvokeWithBytes(method string, params [][]byte, leadingParams ...interface{}) (result interface{}, err error) { _, methodSpec, errGet := rr.serviceRegistry.Get(method) if nil != errGet { return nil, errGet } // Decode the args. _, pInstances := methodSpec.ParamValues() lParamLen := 0 if nil != leadingParams { lParamLen = len(leadingParams) } var _params []string if nil != pInstances && 0 < len(pInstances) { if len(pInstances) != (len(params) + lParamLen) { return nil, fmt.Errorf("count of parameter is not same method[%d] and %d", len(pInstances), (len(params) + lParamLen)) } for indexC := 0; indexC < len(params); indexC++ { ts, err := strconv.Unquote(string(params[indexC])) if nil != err { _params = append(_params, string(params[indexC])) continue } _params = append(_params, ts) } } return rr.Invoke(method, _params, leadingParams...) } // Invoke execute a method. // // Codecs are defined to process a given serialization scheme, e.g., JSON or // XML. A codec is chosen based on the "Content-Type" header from the request, // excluding the charset definition. func (rr *restRegistry) Invoke(method string, params []string, leadingParams ...interface{}) (result interface{}, err error) { serviceSpec, methodSpec, errGet := rr.serviceRegistry.Get(method) if nil != errGet { return nil, errGet } // Decode the args. var in []reflect.Value pValues, pInstances := methodSpec.ParamValues() lParamLen := 0 if nil != leadingParams { lParamLen = len(leadingParams) } if nil != pInstances && 0 < len(pInstances) { if err := cuej.SetValueWithJSONStringArray(params, pInstances[lParamLen:]); nil != err { return nil, err } pCount := len(pInstances) in = make([]reflect.Value, pCount+1) for indexI := lParamLen; indexI < pCount; indexI++ { if pValues[indexI].Type().Kind() == reflect.Ptr && pValues[indexI].Type().Elem().Kind() != reflect.Struct { in[indexI+1] = reflect.Indirect(pValues[indexI]) } else { in[indexI+1] = pValues[indexI] } } } else { in = make([]reflect.Value, 1) } in[0] = serviceSpec.ReceiverValue() for indexI := 0; indexI < lParamLen; indexI++ { in[indexI+1] = reflect.ValueOf(leadingParams[indexI]) } // Call the service method. returnValues := methodSpec.Call(in) if nil != methodSpec.ReturnType() { result = returnValues[0].Interface() if nil != returnValues[1].Interface() { err = returnValues[1].Interface().(error) } } else { if nil != returnValues[0].Interface() { err = returnValues[0].Interface().(error) } } return }