Skip to content

Commit

Permalink
Merge pull request #122 from pantianying/master
Browse files Browse the repository at this point in the history
generic#invoke (go to java)
  • Loading branch information
AlexStocks authored Aug 5, 2019
2 parents 4379d39 + 0c6e36c commit 8d7c1c1
Show file tree
Hide file tree
Showing 8 changed files with 291 additions and 6 deletions.
2 changes: 2 additions & 0 deletions common/constant/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ const (
PREFIX_DEFAULT_KEY = "default."
DEFAULT_SERVICE_FILTERS = "echo"
DEFAULT_REFERENCE_FILTERS = ""
GENERIC_REFERENCE_FILTERS = "generic"
GENERIC = "$invoke"
ECHO = "$echo"
)

Expand Down
1 change: 1 addition & 0 deletions common/constant/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const (
METHODS_KEY = "methods"
TIMEOUT_KEY = "timeout"
BEAN_NAME_KEY = "bean.name"
GENERIC_KEY = "generic"
)

const (
Expand Down
4 changes: 4 additions & 0 deletions config/config_loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ func Load() {
logger.Errorf("[consumer config center refresh] %#v", err)
}
for key, ref := range consumerConfig.References {
if ref.Generic {
genericService := NewGenericService(key)
SetConsumerService(genericService)
}
rpcService := GetConsumerService(key)

if rpcService == nil {
Expand Down
30 changes: 30 additions & 0 deletions config/generic_service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package config

type GenericService struct {
Invoke func(req []interface{}) (interface{}, error) `dubbo:"$invoke"`
referenceStr string
}

func NewGenericService(referenceStr string) *GenericService {
return &GenericService{referenceStr: referenceStr}
}

func (u *GenericService) Reference() string {
return u.referenceStr
}
17 changes: 15 additions & 2 deletions config/reference_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type ReferenceConfig struct {
Params map[string]string `yaml:"params" json:"params,omitempty" property:"params"`
invoker protocol.Invoker
urls []*common.URL
Generic bool `yaml:"generic" json:"generic,omitempty" property:"generic"`
}

func (c *ReferenceConfig) Prefix() string {
Expand Down Expand Up @@ -110,7 +111,6 @@ func (refconfig *ReferenceConfig) Refer() {
regUrl.SubURL = url
}
}

if len(refconfig.urls) == 1 {
refconfig.invoker = extension.GetProtocol(refconfig.urls[0].Protocol).Refer(*refconfig.urls[0])
} else {
Expand Down Expand Up @@ -157,6 +157,7 @@ func (refconfig *ReferenceConfig) getUrlMap() url.Values {
urlMap.Set(constant.RETRIES_KEY, strconv.FormatInt(refconfig.Retries, 10))
urlMap.Set(constant.GROUP_KEY, refconfig.Group)
urlMap.Set(constant.VERSION_KEY, refconfig.Version)
urlMap.Set(constant.GENERIC_KEY, strconv.FormatBool(refconfig.Generic))
//getty invoke async or sync
urlMap.Set(constant.ASYNC_KEY, strconv.FormatBool(refconfig.async))

Expand All @@ -170,7 +171,11 @@ func (refconfig *ReferenceConfig) getUrlMap() url.Values {
urlMap.Set(constant.ENVIRONMENT_KEY, consumerConfig.ApplicationConfig.Environment)

//filter
urlMap.Set(constant.REFERENCE_FILTER_KEY, mergeValue(consumerConfig.Filter, refconfig.Filter, constant.DEFAULT_REFERENCE_FILTERS))
var defaultReferenceFilter = constant.DEFAULT_REFERENCE_FILTERS
if refconfig.Generic {
defaultReferenceFilter = constant.GENERIC_REFERENCE_FILTERS + defaultReferenceFilter
}
urlMap.Set(constant.REFERENCE_FILTER_KEY, mergeValue(consumerConfig.Filter, refconfig.Filter, defaultReferenceFilter))

for _, v := range refconfig.Methods {
urlMap.Set("methods."+v.Name+"."+constant.LOADBALANCE_KEY, v.Loadbalance)
Expand All @@ -180,3 +185,11 @@ func (refconfig *ReferenceConfig) getUrlMap() url.Values {
return urlMap

}
func (refconfig *ReferenceConfig) GenericLoad(id string) {
genericService := NewGenericService(refconfig.id)
SetConsumerService(genericService)
refconfig.id = id
refconfig.Refer()
refconfig.Implement(genericService)
return
}
29 changes: 25 additions & 4 deletions examples/general/dubbo/go-client/app/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,15 @@ import (
"syscall"
"time"
)

import (
"github.com/apache/dubbo-go-hessian2"
hessian "github.com/apache/dubbo-go-hessian2"
)

import (
"github.com/apache/dubbo-go/common/logger"
_ "github.com/apache/dubbo-go/common/proxy/proxy_factory"
"github.com/apache/dubbo-go/config"
_ "github.com/apache/dubbo-go/protocol/dubbo"
"github.com/apache/dubbo-go/protocol/dubbo"
_ "github.com/apache/dubbo-go/registry/protocol"

_ "github.com/apache/dubbo-go/filter/impl"
Expand Down Expand Up @@ -65,7 +64,8 @@ func main() {
test1()
println("\n\ntest2")
test2()

println("\n\ntest3")
test3()
initSignal()
}

Expand Down Expand Up @@ -288,3 +288,24 @@ func test2() {
}
println("error: %v", err)
}
func test3() {
var appName = "UserProviderGer"
var referenceConfig = config.ReferenceConfig{
InterfaceName: "com.ikurento.user.UserProvider",
Cluster: "failover",
Registry: "hangzhouzk",
Protocol: dubbo.DUBBO,
Generic: true,
}
referenceConfig.GenericLoad(appName) //appName is the unique identification of RPCService

time.Sleep(3 * time.Second)
println("\n\n\nstart to generic invoke")
resp, err := referenceConfig.GetRPCService().(*config.GenericService).Invoke([]interface{}{"GetUser", []string{"java.lang.String"}, []interface{}{"A003"}})
if err != nil {
panic(err)
}
println("res: %v\n", resp)
println("succ!")

}
125 changes: 125 additions & 0 deletions filter/impl/generic_filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package impl

import (
"reflect"
"strings"
)
import (
hessian "github.com/apache/dubbo-go-hessian2"
)
import (
"github.com/apache/dubbo-go/common/constant"
"github.com/apache/dubbo-go/common/extension"
"github.com/apache/dubbo-go/filter"
"github.com/apache/dubbo-go/protocol"
invocation2 "github.com/apache/dubbo-go/protocol/invocation"
)

const (
GENERIC = "generic"
)

func init() {
extension.SetFilter(GENERIC, GetGenericFilter)
}

// when do a generic invoke, struct need to be map

type GenericFilter struct{}

func (ef *GenericFilter) Invoke(invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
if invocation.MethodName() == constant.GENERIC && len(invocation.Arguments()) == 3 {
oldArguments := invocation.Arguments()
var newParams []hessian.Object
if oldParams, ok := oldArguments[2].([]interface{}); ok {
for i := range oldParams {
newParams = append(newParams, hessian.Object(struct2MapAll(oldParams[i])))
}
} else {
return invoker.Invoke(invocation)
}
newArguments := []interface{}{
oldArguments[0],
oldArguments[1],
newParams,
}
newInvocation := invocation2.NewRPCInvocation(invocation.MethodName(), newArguments, invocation.Attachments())
newInvocation.SetReply(invocation.Reply())
return invoker.Invoke(newInvocation)
}
return invoker.Invoke(invocation)
}

func (ef *GenericFilter) OnResponse(result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
return result
}

func GetGenericFilter() filter.Filter {
return &GenericFilter{}
}
func struct2MapAll(obj interface{}) interface{} {
if obj == nil {
return obj
}
t := reflect.TypeOf(obj)
v := reflect.ValueOf(obj)
if t.Kind() == reflect.Struct {
result := make(map[string]interface{}, t.NumField())
for i := 0; i < t.NumField(); i++ {
if v.Field(i).Kind() == reflect.Struct {
if v.Field(i).CanInterface() {
setInMap(result, t.Field(i), struct2MapAll(v.Field(i).Interface()))
}
} else if v.Field(i).Kind() == reflect.Slice {
if v.Field(i).CanInterface() {
setInMap(result, t.Field(i), struct2MapAll(v.Field(i).Interface()))
}
} else {
if v.Field(i).CanInterface() {
setInMap(result, t.Field(i), v.Field(i).Interface())
}
}
}
return result
} else if t.Kind() == reflect.Slice {
value := reflect.ValueOf(obj)
var newTemps = make([]interface{}, 0, value.Len())
for i := 0; i < value.Len(); i++ {
newTemp := struct2MapAll(value.Index(i).Interface())
newTemps = append(newTemps, newTemp)
}
return newTemps
} else {
return obj
}
}
func setInMap(m map[string]interface{}, structField reflect.StructField, value interface{}) (result map[string]interface{}) {
result = m
if tagName := structField.Tag.Get("m"); tagName == "" {
result[headerAtoa(structField.Name)] = value
} else {
result[tagName] = value
}
return
}
func headerAtoa(a string) (b string) {
b = strings.ToLower(a[:1]) + a[1:]
return
}
89 changes: 89 additions & 0 deletions filter/impl/generic_filter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package impl

import (
"reflect"
"testing"
)
import (
"github.com/stretchr/testify/assert"
)

func Test_struct2MapAll(t *testing.T) {
var testData struct {
AaAa string `m:"aaAa"`
BaBa string
CaCa struct {
AaAa string
BaBa string `m:"baBa"`
XxYy struct {
xxXx string `m:"xxXx"`
Xx string `m:"xx"`
} `m:"xxYy"`
} `m:"caCa"`
}
testData.AaAa = "1"
testData.BaBa = "1"
testData.CaCa.BaBa = "2"
testData.CaCa.AaAa = "2"
testData.CaCa.XxYy.xxXx = "3"
testData.CaCa.XxYy.Xx = "3"
m := struct2MapAll(testData).(map[string]interface{})
assert.Equal(t, "1", m["aaAa"].(string))
assert.Equal(t, "1", m["baBa"].(string))
assert.Equal(t, "2", m["caCa"].(map[string]interface{})["aaAa"].(string))
assert.Equal(t, "3", m["caCa"].(map[string]interface{})["xxYy"].(map[string]interface{})["xx"].(string))

assert.Equal(t, reflect.Map, reflect.TypeOf(m["caCa"]).Kind())
assert.Equal(t, reflect.Map, reflect.TypeOf(m["caCa"].(map[string]interface{})["xxYy"]).Kind())
}

type testStruct struct {
AaAa string
BaBa string `m:"baBa"`
XxYy struct {
xxXx string `m:"xxXx"`
Xx string `m:"xx"`
} `m:"xxYy"`
}

func Test_struct2MapAll_Slice(t *testing.T) {
var testData struct {
AaAa string `m:"aaAa"`
BaBa string
CaCa []testStruct `m:"caCa"`
}
testData.AaAa = "1"
testData.BaBa = "1"
var tmp testStruct
tmp.BaBa = "2"
tmp.AaAa = "2"
tmp.XxYy.xxXx = "3"
tmp.XxYy.Xx = "3"
testData.CaCa = append(testData.CaCa, tmp)
m := struct2MapAll(testData).(map[string]interface{})

assert.Equal(t, "1", m["aaAa"].(string))
assert.Equal(t, "1", m["baBa"].(string))
assert.Equal(t, "2", m["caCa"].([]interface{})[0].(map[string]interface{})["aaAa"].(string))
assert.Equal(t, "3", m["caCa"].([]interface{})[0].(map[string]interface{})["xxYy"].(map[string]interface{})["xx"].(string))

assert.Equal(t, reflect.Slice, reflect.TypeOf(m["caCa"]).Kind())
assert.Equal(t, reflect.Map, reflect.TypeOf(m["caCa"].([]interface{})[0].(map[string]interface{})["xxYy"]).Kind())
}

0 comments on commit 8d7c1c1

Please sign in to comment.