This commit is contained in:
crusader 2018-04-10 18:07:52 +09:00
commit 558c2aba94
5 changed files with 267 additions and 0 deletions

68
.gitignore vendored Normal file
View File

@ -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

32
.vscode/launch.json vendored Normal file
View File

@ -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
}
]
}

5
glide.yaml Normal file
View File

@ -0,0 +1,5 @@
package: git.loafle.net/commons/rest-go
import:
- package: git.loafle.net/commons/util-go
subpackages:
- encoding/json

6
registry/registry.go Normal file
View File

@ -0,0 +1,6 @@
package registry
type RESTInvoker interface {
HasMethod(method string) bool
Invoke(method string, params []string, leadingParams ...interface{}) (result interface{}, err error)
}

156
registry/rest-registry.go Normal file
View File

@ -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
}