194 lines
5.3 KiB
Go
194 lines
5.3 KiB
Go
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.
|
|
|
|
pValues, 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++ {
|
|
if pValues[indexC+lParamLen].Type().Kind() != reflect.String {
|
|
_params = append(_params, string(params[indexC]))
|
|
continue
|
|
}
|
|
ts, err := strconv.Unquote(string(params[indexC]))
|
|
if nil != err {
|
|
return nil, err
|
|
}
|
|
_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
|
|
}
|