commit 558c2aba9447846339b954f271f26ffbc4cf0a45 Author: crusader Date: Tue Apr 10 18:07:52 2018 +0900 ing diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3733e36 --- /dev/null +++ b/.gitignore @@ -0,0 +1,68 @@ +# Created by .ignore support plugin (hsz.mobi) +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries + +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties +### Go template +# Binaries for programs and plugins +*.exe +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 +.glide/ +.idea/ +*.iml + +vendor/ +glide.lock +.DS_Store +dist/ +debug diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..2ca2b1d --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,32 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug", + "type": "go", + "request": "launch", + "mode": "debug", + "remotePath": "", + "port": 2345, + "host": "127.0.0.1", + "program": "${workspaceRoot}/main.go", + "env": {}, + "args": [], + "showLog": true + }, + { + "name": "File Debug", + "type": "go", + "request": "launch", + "mode": "debug", + "remotePath": "", + "port": 2345, + "host": "127.0.0.1", + "program": "${fileDirname}", + "env": {}, + "args": [], + "showLog": true + } + + ] +} \ No newline at end of file diff --git a/glide.yaml b/glide.yaml new file mode 100644 index 0000000..104c28a --- /dev/null +++ b/glide.yaml @@ -0,0 +1,5 @@ +package: git.loafle.net/commons/rest-go +import: +- package: git.loafle.net/commons/util-go + subpackages: + - encoding/json diff --git a/registry/registry.go b/registry/registry.go new file mode 100644 index 0000000..7d4c18f --- /dev/null +++ b/registry/registry.go @@ -0,0 +1,6 @@ +package registry + +type RESTInvoker interface { + HasMethod(method string) bool + Invoke(method string, params []string, leadingParams ...interface{}) (result interface{}, err error) +} diff --git a/registry/rest-registry.go b/registry/rest-registry.go new file mode 100644 index 0000000..cf4b1a4 --- /dev/null +++ b/registry/rest-registry.go @@ -0,0 +1,156 @@ +package registry + +import ( + "reflect" + + 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) 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() + var lParamLen int + if nil != leadingParams { + lParamLen = len(leadingParams) + } + + if nil != pInstances && 0 < len(pInstances) { + if err := cuej.SetValueWithJSONStringArray(params, pInstances[lParamLen:]); nil != err { + return nil, err + } + + for indexI := 0; indexI < lParamLen; indexI++ { + i := pInstances[indexI] + rv := reflect.ValueOf(i) + iv := reflect.ValueOf(leadingParams[indexI]) + switch reflect.TypeOf(i).Elem().Kind() { + case reflect.Struct: + rv.Elem().Set(iv.Elem()) + default: + rv.Elem().Set(iv) + } + } + + pCount := len(pInstances) + in = make([]reflect.Value, pCount+1) + + for indexI := 0; 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() + + // 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 +}