This is an automated email from the ASF dual-hosted git repository.

alexstocks pushed a commit to branch 1.5
in repository https://gitbox.apache.org/repos/asf/dubbo-go.git


The following commit(s) were added to refs/heads/1.5 by this push:
     new 826f525  fix generic call bug (#1413)
826f525 is described below

commit 826f525903f031d97c2ee051dfc2de38781d83e1
Author: alchemy-lee <[email protected]>
AuthorDate: Fri Aug 27 22:13:36 2021 +0800

    fix generic call bug (#1413)
---
 filter/filter_impl/generic_filter.go               | 183 +++++------------
 filter/filter_impl/generic_filter_test.go          | 191 +++++++----------
 ...eneric_filter.go => generic_generalizer_map.go} |  82 ++++----
 ...ter_test.go => generic_generalizer_map_test.go} | 156 +++++++++++---
 filter/filter_impl/generic_service_filter.go       | 111 +++++-----
 filter/filter_impl/generic_service_filter_test.go  | 228 ++++++++++++---------
 protocol/dubbo/hessian2/hessian_request.go         |  91 +-------
 protocol/dubbo/hessian2/java_class.go              | 200 ++++++++++++++++++
 protocol/dubbo/hessian2/java_class_test.go         | 132 ++++++++++++
 9 files changed, 802 insertions(+), 572 deletions(-)

diff --git a/filter/filter_impl/generic_filter.go 
b/filter/filter_impl/generic_filter.go
index 86f8cfd..facdfda 100644
--- a/filter/filter_impl/generic_filter.go
+++ b/filter/filter_impl/generic_filter.go
@@ -19,9 +19,7 @@ package filter_impl
 
 import (
        "context"
-       "reflect"
        "strings"
-       "time"
 )
 
 import (
@@ -54,23 +52,47 @@ type GenericFilter struct{}
 
 // Invoke turns the parameters to map for generic method
 func (ef *GenericFilter) Invoke(ctx context.Context, invoker protocol.Invoker, 
invocation protocol.Invocation) protocol.Result {
-       if invocation.MethodName() == constant.GENERIC && 
len(invocation.Arguments()) == 3 {
-               oldArguments := invocation.Arguments()
+       if isCallingToGenericService(invoker, invocation) {
 
-               if oldParams, ok := oldArguments[2].([]interface{}); ok {
-                       newParams := make([]hessian.Object, 0, len(oldParams))
-                       for i := range oldParams {
-                               newParams = append(newParams, 
hessian.Object(objToMap(oldParams[i])))
+               mtdname := invocation.MethodName()
+               oldargs := invocation.Arguments()
+
+               types := make([]string, 0, len(oldargs))
+               args := make([]hessian.Object, 0, len(oldargs))
+
+               // get generic info from attachments of invocation, the default 
value is "true"
+               //generic := invocation.AttachmentsByKey(constant.GENERIC_KEY, 
GENERIC_SERIALIZATION_DEFAULT)
+               // get generalizer according to value in the `generic`
+               g := GetMapGeneralizer()
+
+               for _, arg := range oldargs {
+                       // use the default generalizer(MapGeneralizer)
+                       typ, err := g.GetType(arg)
+                       if err != nil {
+                               logger.Errorf("failed to get type, %v", err)
                        }
-                       newArguments := []interface{}{
-                               oldArguments[0],
-                               oldArguments[1],
-                               newParams,
+                       obj, err := g.Generalize(arg)
+                       if err != nil {
+                               logger.Errorf("generalization failed, %v", err)
+                               return invoker.Invoke(ctx, invocation)
                        }
-                       newInvocation := 
invocation2.NewRPCInvocation(invocation.MethodName(), newArguments, 
invocation.Attachments())
-                       newInvocation.SetReply(invocation.Reply())
-                       return invoker.Invoke(ctx, newInvocation)
+                       types = append(types, typ)
+                       args = append(args, obj)
                }
+
+               // construct a new invocation for generic call
+               newargs := []interface{}{
+                       mtdname,
+                       types,
+                       args,
+               }
+               newivc := invocation2.NewRPCInvocation(constant.GENERIC, 
newargs, invocation.Attachments())
+               newivc.SetReply(invocation.Reply())
+               newivc.Attachments()[constant.GENERIC_KEY] = 
invoker.GetURL().GetParam(constant.GENERIC_KEY, "")
+
+               return invoker.Invoke(ctx, newivc)
+       } else if isMakingAGenericCall(invoker, invocation) {
+               invocation.Attachments()[constant.GENERIC_KEY] = 
invoker.GetURL().GetParam(constant.GENERIC_KEY, "")
        }
        return invoker.Invoke(ctx, invocation)
 }
@@ -86,125 +108,22 @@ func GetGenericFilter() filter.Filter {
        return &GenericFilter{}
 }
 
-func objToMap(obj interface{}) interface{} {
-       if obj == nil {
-               return obj
-       }
-
-       t := reflect.TypeOf(obj)
-       v := reflect.ValueOf(obj)
-
-       // if obj is a POJO, get the struct from the pointer (if it is a 
pointer)
-       pojo, isPojo := obj.(hessian.POJO)
-       if isPojo {
-               for t.Kind() == reflect.Ptr {
-                       t = t.Elem()
-                       v = v.Elem()
-               }
-       }
-
-       switch t.Kind() {
-       case reflect.Struct:
-               result := make(map[string]interface{}, t.NumField())
-               if isPojo {
-                       result["class"] = pojo.JavaClassName()
-               }
-               for i := 0; i < t.NumField(); i++ {
-                       field := t.Field(i)
-                       value := v.Field(i)
-                       kind := value.Kind()
-                       if !value.CanInterface() {
-                               logger.Debugf("objToMap for %v is skipped 
because it couldn't be converted to interface", field)
-                               continue
-                       }
-                       valueIface := value.Interface()
-                       switch kind {
-                       case reflect.Ptr:
-                               if value.IsNil() {
-                                       setInMap(result, field, nil)
-                                       continue
-                               }
-                               setInMap(result, field, objToMap(valueIface))
-                       case reflect.Struct, reflect.Slice, reflect.Map:
-                               if isPrimitive(valueIface) {
-                                       logger.Warnf("\"%s\" is primitive. The 
application may crash if it's transferred between "+
-                                               "systems implemented by 
different languages, e.g. dubbo-go <-> dubbo-java. We recommend "+
-                                               "you represent the object by 
basic types, like string.", value.Type())
-                                       setInMap(result, field, valueIface)
-                                       continue
-                               }
-
-                               setInMap(result, field, objToMap(valueIface))
-                       default:
-                               setInMap(result, field, valueIface)
-                       }
-               }
-               return result
-       case reflect.Array, reflect.Slice:
-               value := reflect.ValueOf(obj)
-               newTemps := make([]interface{}, 0, value.Len())
-               for i := 0; i < value.Len(); i++ {
-                       newTemp := objToMap(value.Index(i).Interface())
-                       newTemps = append(newTemps, newTemp)
-               }
-               return newTemps
-       case reflect.Map:
-               newTempMap := make(map[interface{}]interface{}, v.Len())
-               iter := v.MapRange()
-               for iter.Next() {
-                       if !iter.Value().CanInterface() {
-                               continue
-                       }
-                       key := iter.Key()
-                       mapV := iter.Value().Interface()
-                       newTempMap[mapKey(key)] = objToMap(mapV)
-               }
-               return newTempMap
-       case reflect.Ptr:
-               return objToMap(v.Elem().Interface())
-       default:
-               return obj
-       }
-}
-
-// mapKey converts the map key to interface type
-func mapKey(key reflect.Value) interface{} {
-       switch key.Kind() {
-       case reflect.Bool, reflect.Int, reflect.Int8,
-               reflect.Int16, reflect.Int32, reflect.Int64,
-               reflect.Uint, reflect.Uint8, reflect.Uint16,
-               reflect.Uint32, reflect.Uint64, reflect.Float32,
-               reflect.Float64, reflect.String:
-               return key.Interface()
-       default:
-               name := key.String()
-               if name == "class" {
-                       panic(`"class" is a reserved keyword`)
-               }
-               return name
-       }
+// isCallingToGenericService check if it calls to a generic service
+func isCallingToGenericService(invoker protocol.Invoker, invocation 
protocol.Invocation) bool {
+       return isGeneric(invoker.GetURL().GetParam(constant.GENERIC_KEY, "")) &&
+               invocation.MethodName() != constant.GENERIC
 }
 
-// setInMap sets the struct into the map using the tag or the name of the 
struct as the key
-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[toUnexport(structField.Name)] = value
-       } else {
-               result[tagName] = value
-       }
-       return
+// isMakingAGenericCall check if it is making a generic call to a generic 
service
+func isMakingAGenericCall(invoker protocol.Invoker, invocation 
protocol.Invocation) bool {
+       return isGeneric(invoker.GetURL().GetParam(constant.GENERIC_KEY, "")) &&
+               invocation.MethodName() == constant.GENERIC &&
+               invocation.Arguments() != nil &&
+               len(invocation.Arguments()) == 3
 }
 
-// toUnexport is to lower the first letter
-func toUnexport(a string) string {
-       return strings.ToLower(a[:1]) + a[1:]
-}
-
-// isPrimitive determines if the object is primitive
-func isPrimitive(obj interface{}) bool {
-       if _, ok := obj.(time.Time); ok {
-               return true
-       }
-       return false
+// isGeneric receives a generic field from url of invoker to determine whether 
the service is generic or not
+func isGeneric(generic string) bool {
+       lowerGeneric := strings.ToLower(generic)
+       return lowerGeneric == GENERIC_SERIALIZATION_DEFAULT
 }
diff --git a/filter/filter_impl/generic_filter_test.go 
b/filter/filter_impl/generic_filter_test.go
index 4a1f12e..bc861fd 100644
--- a/filter/filter_impl/generic_filter_test.go
+++ b/filter/filter_impl/generic_filter_test.go
@@ -18,136 +18,83 @@
 package filter_impl
 
 import (
-       "reflect"
+       "context"
+       "net/url"
        "testing"
-       "time"
 )
 
 import (
+       hessian "github.com/apache/dubbo-go-hessian2"
+       "github.com/golang/mock/gomock"
        "github.com/stretchr/testify/assert"
 )
 
-func TestStruct2MapAll(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"`
-               DaDa time.Time
-               EeEe int
-       }
-       testData.AaAa = "1"
-       testData.BaBa = "1"
-       testData.CaCa.BaBa = "2"
-       testData.CaCa.AaAa = "2"
-       testData.CaCa.XxYy.xxXx = "3"
-       testData.CaCa.XxYy.Xx = "3"
-       testData.DaDa = time.Date(2020, 10, 29, 2, 34, 0, 0, time.Local)
-       testData.EeEe = 100
-       m := objToMap(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())
-       assert.Equal(t, "2020-10-29 02:34:00", 
m["daDa"].(time.Time).Format("2006-01-02 15:04:05"))
-       assert.Equal(t, 100, m["eeEe"].(int))
-}
-
-type testStruct struct {
-       AaAa string
-       BaBa string `m:"baBa"`
-       XxYy struct {
-               xxXx string `m:"xxXx"`
-               Xx   string `m:"xx"`
-       } `m:"xxYy"`
-}
-
-func TestStruct2MapAllSlice(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 := objToMap(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())
-}
-
-func TestStruct2MapAllMap(t *testing.T) {
-       var testData struct {
-               AaAa   string
-               Baba   map[string]interface{}
-               CaCa   map[string]string
-               DdDd   map[string]interface{}
-               IntMap map[int]interface{}
-       }
-       testData.AaAa = "aaaa"
-       testData.Baba = make(map[string]interface{})
-       testData.CaCa = make(map[string]string)
-       testData.DdDd = nil
-       testData.IntMap = make(map[int]interface{})
-
-       testData.Baba["kk"] = 1
-       var structData struct {
-               Str string
-       }
-       structData.Str = "str"
-       testData.Baba["struct"] = structData
-       testData.Baba["nil"] = nil
-       testData.CaCa["k1"] = "v1"
-       testData.CaCa["kv2"] = "v2"
-       testData.IntMap[1] = 1
-       m := objToMap(testData)
-
-       assert.Equal(t, reflect.Map, reflect.TypeOf(m).Kind())
-       mappedStruct := m.(map[string]interface{})
-       assert.Equal(t, reflect.String, 
reflect.TypeOf(mappedStruct["aaAa"]).Kind())
-       assert.Equal(t, reflect.Map, 
reflect.TypeOf(mappedStruct["baba"]).Kind())
-       assert.Equal(t, reflect.Map, 
reflect.TypeOf(mappedStruct["baba"].(map[interface{}]interface{})["struct"]).Kind())
-       assert.Equal(t, "str", 
mappedStruct["baba"].(map[interface{}]interface{})["struct"].(map[string]interface{})["str"])
-       assert.Equal(t, nil, 
mappedStruct["baba"].(map[interface{}]interface{})["nil"])
-       assert.Equal(t, reflect.Map, 
reflect.TypeOf(mappedStruct["caCa"]).Kind())
-       assert.Equal(t, reflect.Map, 
reflect.TypeOf(mappedStruct["ddDd"]).Kind())
-       intMap := mappedStruct["intMap"]
-       assert.Equal(t, reflect.Map, reflect.TypeOf(intMap).Kind())
-       assert.Equal(t, 1, intMap.(map[interface{}]interface{})[1])
-}
-
-type mockParent struct {
-       Children []*mockChild
-}
-
-func (p *mockParent) JavaClassName() string {
-       return "org.apache.dubbo.mockParent"
-}
+import (
+       "github.com/apache/dubbo-go/common"
+       "github.com/apache/dubbo-go/common/constant"
+       "github.com/apache/dubbo-go/protocol"
+       "github.com/apache/dubbo-go/protocol/invocation"
+       "github.com/apache/dubbo-go/protocol/mock"
+)
 
-type mockChild struct {
-       Name string
+// test isCallingToGenericService branch
+func TestFilter_Invoke(t *testing.T) {
+       invokeUrl := common.NewURLWithOptions(
+               common.WithParams(url.Values{}),
+               common.WithParamsValue(constant.GENERIC_KEY, 
GENERIC_SERIALIZATION_DEFAULT))
+       filter := &GenericFilter{}
+
+       ctrl := gomock.NewController(t)
+       defer ctrl.Finish()
+
+       normalInvocation := invocation.NewRPCInvocation("Hello", 
[]interface{}{"arg1"}, make(map[string]interface{}))
+
+       mockInvoker := mock.NewMockInvoker(ctrl)
+       mockInvoker.EXPECT().GetURL().Return(invokeUrl).Times(2)
+       mockInvoker.EXPECT().Invoke(gomock.Not(normalInvocation)).DoAndReturn(
+               func(invocation protocol.Invocation) protocol.Result {
+                       assert.Equal(t, constant.GENERIC, 
invocation.MethodName())
+                       args := invocation.Arguments()
+                       assert.Equal(t, "Hello", args[0])
+                       assert.Equal(t, "java.lang.String", 
args[1].([]string)[0])
+                       assert.Equal(t, "arg1", 
args[2].([]hessian.Object)[0].(string))
+                       assert.Equal(t, GENERIC_SERIALIZATION_DEFAULT, 
invocation.AttachmentsByKey(constant.GENERIC_KEY, ""))
+                       return &protocol.RPCResult{}
+               })
+
+       result := filter.Invoke(context.Background(), mockInvoker, 
normalInvocation)
+       assert.NotNil(t, result)
 }
 
-func (p *mockChild) JavaClassName() string {
-       return "org.apache.dubbo.mockChild"
+// test isMakingAGenericCall branch
+func TestFilter_InvokeWithGenericCall(t *testing.T) {
+       invokeUrl := common.NewURLWithOptions(
+               common.WithParams(url.Values{}),
+               common.WithParamsValue(constant.GENERIC_KEY, 
GENERIC_SERIALIZATION_DEFAULT))
+       filter := &GenericFilter{}
+
+       ctrl := gomock.NewController(t)
+       defer ctrl.Finish()
+
+       genericInvocation := invocation.NewRPCInvocation(constant.GENERIC, 
[]interface{}{
+               "hello",
+               []string{"java.lang.String"},
+               []string{"arg1"},
+       }, make(map[string]interface{}))
+
+       mockInvoker := mock.NewMockInvoker(ctrl)
+       mockInvoker.EXPECT().GetURL().Return(invokeUrl).Times(3)
+       mockInvoker.EXPECT().Invoke(gomock.Any()).DoAndReturn(
+               func(invocation protocol.Invocation) protocol.Result {
+                       assert.Equal(t, constant.GENERIC, 
invocation.MethodName())
+                       args := invocation.Arguments()
+                       assert.Equal(t, "hello", args[0])
+                       assert.Equal(t, "java.lang.String", 
args[1].([]string)[0])
+                       assert.Equal(t, "arg1", args[2].([]string)[0])
+                       assert.Equal(t, GENERIC_SERIALIZATION_DEFAULT, 
invocation.AttachmentsByKey(constant.GENERIC_KEY, ""))
+                       return &protocol.RPCResult{}
+               })
+
+       result := filter.Invoke(context.Background(), mockInvoker, 
genericInvocation)
+       assert.NotNil(t, result)
 }
diff --git a/filter/filter_impl/generic_filter.go 
b/filter/filter_impl/generic_generalizer_map.go
similarity index 72%
copy from filter/filter_impl/generic_filter.go
copy to filter/filter_impl/generic_generalizer_map.go
index 86f8cfd..73531a3 100644
--- a/filter/filter_impl/generic_filter.go
+++ b/filter/filter_impl/generic_generalizer_map.go
@@ -18,74 +18,70 @@
 package filter_impl
 
 import (
-       "context"
        "reflect"
        "strings"
+       "sync"
        "time"
 )
 
 import (
        hessian "github.com/apache/dubbo-go-hessian2"
+       "github.com/mitchellh/mapstructure"
+       perrors "github.com/pkg/errors"
 )
 
 import (
-       "github.com/apache/dubbo-go/common/constant"
-       "github.com/apache/dubbo-go/common/extension"
        "github.com/apache/dubbo-go/common/logger"
-       "github.com/apache/dubbo-go/filter"
-       "github.com/apache/dubbo-go/protocol"
-       invocation2 "github.com/apache/dubbo-go/protocol/invocation"
+       "github.com/apache/dubbo-go/protocol/dubbo/hessian2"
 )
 
-const (
-       // GENERIC
-       //generic module name
-       GENERIC = "generic"
+var (
+       mapGeneralizer     *MapGeneralizer
+       mapGeneralizerOnce sync.Once
 )
 
-func init() {
-       extension.SetFilter(GENERIC, GetGenericFilter)
+func GetMapGeneralizer() *MapGeneralizer {
+       mapGeneralizerOnce.Do(func() {
+               mapGeneralizer = &MapGeneralizer{}
+       })
+       return mapGeneralizer
 }
 
-//  when do a generic invoke, struct need to be map
+type MapGeneralizer struct{}
 
-// nolint
-type GenericFilter struct{}
-
-// Invoke turns the parameters to map for generic method
-func (ef *GenericFilter) Invoke(ctx context.Context, invoker protocol.Invoker, 
invocation protocol.Invocation) protocol.Result {
-       if invocation.MethodName() == constant.GENERIC && 
len(invocation.Arguments()) == 3 {
-               oldArguments := invocation.Arguments()
+func (g *MapGeneralizer) Generalize(obj interface{}) (gobj interface{}, err 
error) {
+       gobj = objToMap(obj)
+       return
+}
 
-               if oldParams, ok := oldArguments[2].([]interface{}); ok {
-                       newParams := make([]hessian.Object, 0, len(oldParams))
-                       for i := range oldParams {
-                               newParams = append(newParams, 
hessian.Object(objToMap(oldParams[i])))
-                       }
-                       newArguments := []interface{}{
-                               oldArguments[0],
-                               oldArguments[1],
-                               newParams,
-                       }
-                       newInvocation := 
invocation2.NewRPCInvocation(invocation.MethodName(), newArguments, 
invocation.Attachments())
-                       newInvocation.SetReply(invocation.Reply())
-                       return invoker.Invoke(ctx, newInvocation)
-               }
+func (g *MapGeneralizer) Realize(obj interface{}, typ reflect.Type) 
(interface{}, error) {
+       newobj := reflect.New(typ).Interface()
+       err := mapstructure.Decode(obj, newobj)
+       if err != nil {
+               return nil, perrors.Errorf("realizing map failed, %v", err)
        }
-       return invoker.Invoke(ctx, invocation)
-}
 
-// OnResponse dummy process, returns the result directly
-func (ef *GenericFilter) OnResponse(_ context.Context, result protocol.Result, 
_ protocol.Invoker,
-       _ protocol.Invocation) protocol.Result {
-       return result
+       return reflect.ValueOf(newobj).Elem().Interface(), nil
 }
 
-// GetGenericFilter returns GenericFilter instance
-func GetGenericFilter() filter.Filter {
-       return &GenericFilter{}
+func (g *MapGeneralizer) GetType(obj interface{}) (typ string, err error) {
+       typ, err = hessian2.GetJavaName(obj)
+       // no error or error is not NilError
+       if err == nil || err != hessian2.NilError {
+               return
+       }
+
+       typ = "java.lang.Object"
+       if err == hessian2.NilError {
+               logger.Debugf("the type of nil object couldn't be inferred, use 
the default value(\"%s\")", typ)
+               return
+       }
+
+       logger.Debugf("the type of object(=%T) couldn't be recognized as a 
POJO, use the default value(\"%s\")", obj, typ)
+       return
 }
 
+// objToMap converts an object(interface{}) to a map
 func objToMap(obj interface{}) interface{} {
        if obj == nil {
                return obj
diff --git a/filter/filter_impl/generic_filter_test.go 
b/filter/filter_impl/generic_generalizer_map_test.go
similarity index 53%
copy from filter/filter_impl/generic_filter_test.go
copy to filter/filter_impl/generic_generalizer_map_test.go
index 4a1f12e..cb072d1 100644
--- a/filter/filter_impl/generic_filter_test.go
+++ b/filter/filter_impl/generic_generalizer_map_test.go
@@ -27,30 +27,32 @@ import (
        "github.com/stretchr/testify/assert"
 )
 
-func TestStruct2MapAll(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"`
-               DaDa time.Time
-               EeEe int
-       }
-       testData.AaAa = "1"
-       testData.BaBa = "1"
-       testData.CaCa.BaBa = "2"
-       testData.CaCa.AaAa = "2"
-       testData.CaCa.XxYy.xxXx = "3"
-       testData.CaCa.XxYy.Xx = "3"
-       testData.DaDa = time.Date(2020, 10, 29, 2, 34, 0, 0, time.Local)
-       testData.EeEe = 100
-       m := objToMap(testData).(map[string]interface{})
+type testPlainObj 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"`
+       DaDa time.Time
+       EeEe int
+}
+
+func TestObjToMap(t *testing.T) {
+       obj := &testPlainObj{}
+       obj.AaAa = "1"
+       obj.BaBa = "1"
+       obj.CaCa.BaBa = "2"
+       obj.CaCa.AaAa = "2"
+       obj.CaCa.XxYy.xxXx = "3"
+       obj.CaCa.XxYy.Xx = "3"
+       obj.DaDa = time.Date(2020, 10, 29, 2, 34, 0, 0, time.Local)
+       obj.EeEe = 100
+       m := objToMap(obj).(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))
@@ -71,7 +73,7 @@ type testStruct struct {
        } `m:"xxYy"`
 }
 
-func TestStruct2MapAllSlice(t *testing.T) {
+func TestObjToMap_Slice(t *testing.T) {
        var testData struct {
                AaAa string `m:"aaAa"`
                BaBa string
@@ -96,7 +98,7 @@ func TestStruct2MapAllSlice(t *testing.T) {
        assert.Equal(t, reflect.Map, 
reflect.TypeOf(m["caCa"].([]interface{})[0].(map[string]interface{})["xxYy"]).Kind())
 }
 
-func TestStruct2MapAllMap(t *testing.T) {
+func TestObjToMap_Map(t *testing.T) {
        var testData struct {
                AaAa   string
                Baba   map[string]interface{}
@@ -136,18 +138,112 @@ func TestStruct2MapAllMap(t *testing.T) {
        assert.Equal(t, 1, intMap.(map[interface{}]interface{})[1])
 }
 
+var mockMapGeneralizer = GetMapGeneralizer()
+
 type mockParent struct {
-       Children []*mockChild
+       Gender, Email, Name string
+       Age                 int
+       Child               *mockChild
 }
 
-func (p *mockParent) JavaClassName() string {
+func (p mockParent) JavaClassName() string {
        return "org.apache.dubbo.mockParent"
 }
 
 type mockChild struct {
-       Name string
+       Gender, Email, Name string
+       Age                 int
 }
 
-func (p *mockChild) JavaClassName() string {
+func (c *mockChild) JavaClassName() string {
        return "org.apache.dubbo.mockChild"
 }
+
+func TestPOJOClassName(t *testing.T) {
+       c := &mockChild{
+               Age:    20,
+               Gender: "male",
+               Email:  "[email protected]",
+               Name:   "lmc",
+       }
+       p := mockParent{
+               Age:    30,
+               Gender: "male",
+               Email:  "[email protected]",
+               Name:   "xavierniu",
+               Child:  c,
+       }
+
+       m, err := mockMapGeneralizer.Generalize(p)
+       assert.Nil(t, err)
+       // parent
+       assert.Equal(t, "xavierniu", 
m.(map[string]interface{})["name"].(string))
+       assert.Equal(t, 30, m.(map[string]interface{})["age"].(int))
+       assert.Equal(t, "org.apache.dubbo.mockParent", 
m.(map[string]interface{})["class"].(string))
+       // child
+       assert.Equal(t, 20, 
m.(map[string]interface{})["child"].(map[string]interface{})["age"].(int))
+       assert.Equal(t, "lmc", 
m.(map[string]interface{})["child"].(map[string]interface{})["name"].(string))
+       assert.Equal(t, "org.apache.dubbo.mockChild", 
m.(map[string]interface{})["child"].(map[string]interface{})["class"].(string))
+
+       r, err := mockMapGeneralizer.Realize(m, reflect.TypeOf(p))
+       assert.Nil(t, err)
+       rMockParent, ok := r.(mockParent)
+       assert.True(t, ok)
+       // parent
+       assert.Equal(t, "xavierniu", rMockParent.Name)
+       assert.Equal(t, 30, rMockParent.Age)
+       // child
+       assert.Equal(t, "lmc", rMockParent.Child.Name)
+       assert.Equal(t, 20, rMockParent.Child.Age)
+}
+
+func TestPOJOArray(t *testing.T) {
+       c1 := &mockChild{
+               Age:    20,
+               Gender: "male",
+               Email:  "[email protected]",
+               Name:   "lmc",
+       }
+       c2 := &mockChild{
+               Age:    21,
+               Gender: "male",
+               Email:  "[email protected]",
+               Name:   "lmc1",
+       }
+
+       pojoArr := []*mockChild{c1, c2}
+
+       m, err := mockMapGeneralizer.Generalize(pojoArr)
+       assert.Nil(t, err)
+       assert.Equal(t, "lmc", 
m.([]interface{})[0].(map[string]interface{})["name"].(string))
+       assert.Equal(t, 20, 
m.([]interface{})[0].(map[string]interface{})["age"].(int))
+       assert.Equal(t, "lmc1", 
m.([]interface{})[1].(map[string]interface{})["name"].(string))
+       assert.Equal(t, 21, 
m.([]interface{})[1].(map[string]interface{})["age"].(int))
+
+       r, err := mockMapGeneralizer.Realize(m, reflect.TypeOf(pojoArr))
+       assert.Nil(t, err)
+       rPojoArr, ok := r.([]*mockChild)
+       assert.True(t, ok)
+       assert.Equal(t, "lmc", rPojoArr[0].Name)
+       assert.Equal(t, 20, rPojoArr[0].Age)
+       assert.Equal(t, "lmc1", rPojoArr[1].Name)
+       assert.Equal(t, 21, rPojoArr[1].Age)
+}
+
+func TestNullField(t *testing.T) {
+       p := mockParent{
+               Age:    30,
+               Gender: "male",
+               Email:  "[email protected]",
+               Name:   "xavierniu",
+       }
+
+       m, _ := mockMapGeneralizer.Generalize(p)
+       assert.Nil(t, m.(map[string]interface{})["child"])
+
+       r, err := mockMapGeneralizer.Realize(m, reflect.TypeOf(p))
+       assert.Nil(t, err)
+       rMockParent, ok := r.(mockParent)
+       assert.True(t, ok)
+       assert.Nil(t, rMockParent.Child)
+}
diff --git a/filter/filter_impl/generic_service_filter.go 
b/filter/filter_impl/generic_service_filter.go
index 6056224..886bb05 100644
--- a/filter/filter_impl/generic_service_filter.go
+++ b/filter/filter_impl/generic_service_filter.go
@@ -19,12 +19,10 @@ package filter_impl
 
 import (
        "context"
-       "reflect"
 )
 
 import (
        hessian "github.com/apache/dubbo-go-hessian2"
-       "github.com/mitchellh/mapstructure"
        perrors "github.com/pkg/errors"
 )
 
@@ -54,74 +52,79 @@ type GenericServiceFilter struct{}
 
 // Invoke is used to call service method by invocation
 func (ef *GenericServiceFilter) Invoke(ctx context.Context, invoker 
protocol.Invoker, invocation protocol.Invocation) protocol.Result {
-       logger.Infof("invoking generic service filter.")
-       logger.Debugf("generic service filter methodName:%v,args:%v", 
invocation.MethodName(), len(invocation.Arguments()))
-
        if invocation.MethodName() != constant.GENERIC || 
len(invocation.Arguments()) != 3 {
                return invoker.Invoke(ctx, invocation)
        }
 
-       var (
-               ok         bool
-               err        error
-               methodName string
-               newParams  []interface{}
-               genericKey string
-               argsType   []reflect.Type
-               oldParams  []hessian.Object
-       )
-
-       url := invoker.GetURL()
-       methodName = invocation.Arguments()[0].(string)
-       // get service
-       svc := common.ServiceMap.GetServiceByServiceKey(url.Protocol, 
url.ServiceKey())
-       // get method
-       method := svc.Method()[methodName]
+       // get real invocation info from the generic invocation
+       mtdname := invocation.Arguments()[0].(string)
+       // types are not required in dubbo-go, for dubbo-go client to dubbo-go 
server, types could be nil
+       types := invocation.Arguments()[1]
+       args := invocation.Arguments()[2].([]hessian.Object)
+
+       logger.Debugf(`received a generic invocation: 
+               MethodName: %s,
+               Types: %s,
+               Args: %s
+       `, mtdname, types, args)
+
+       // get the type of the argument
+       ivkUrl := invoker.GetURL()
+       svc := common.ServiceMap.GetServiceByServiceKey(ivkUrl.Protocol, 
ivkUrl.ServiceKey())
+       method := svc.Method()[mtdname]
        if method == nil {
-               logger.Errorf("[Generic Service Filter] Don't have this method: 
%s", methodName)
-               return &protocol.RPCResult{}
-       }
-       argsType = method.ArgsType()
-       genericKey = invocation.AttachmentsByKey(constant.GENERIC_KEY, 
GENERIC_SERIALIZATION_DEFAULT)
-       if genericKey == GENERIC_SERIALIZATION_DEFAULT {
-               oldParams, ok = invocation.Arguments()[2].([]hessian.Object)
-       } else {
-               logger.Errorf("[Generic Service Filter] Don't support this 
generic: %s", genericKey)
-               return &protocol.RPCResult{}
-       }
-       if !ok {
-               logger.Errorf("[Generic Service Filter] wrong serialization")
-               return &protocol.RPCResult{}
+               return &protocol.RPCResult{
+                       Err: perrors.Errorf("\"%s\" method is not found, 
service key: %s", mtdname, ivkUrl.ServiceKey()),
+               }
        }
-       if len(oldParams) != len(argsType) {
-               logger.Errorf("[Generic Service Filter] method:%s invocation 
arguments number was wrong", methodName)
-               return &protocol.RPCResult{}
+       argsType := method.ArgsType()
+
+       // get generic info from attachments of invocation, the default value 
is "true"
+       //generic := invocation.AttachmentsByKey(constant.GENERIC_KEY, 
GENERIC_SERIALIZATION_DEFAULT)
+       // get generalizer according to value in the `generic`
+       g := GetMapGeneralizer()
+
+       if len(args) != len(argsType) {
+               return &protocol.RPCResult{
+                       Err: perrors.Errorf("the number of args(=%d) is not 
matched with \"%s\" method", len(args), mtdname),
+               }
        }
-       // oldParams convert to newParams
-       newParams = make([]interface{}, len(oldParams))
-       for i := range argsType {
-               newParam := reflect.New(argsType[i]).Interface()
-               err = mapstructure.Decode(oldParams[i], newParam)
-               newParam = reflect.ValueOf(newParam).Elem().Interface()
+
+       // realize
+       newargs := make([]interface{}, len(argsType))
+       for i := 0; i < len(argsType); i++ {
+               newarg, err := g.Realize(args[i], argsType[i])
                if err != nil {
-                       logger.Errorf("[Generic Service Filter] decode 
arguments map to struct wrong: error{%v}", perrors.WithStack(err))
-                       return &protocol.RPCResult{}
+                       return &protocol.RPCResult{
+                               Err: perrors.Errorf("realization failed, %v", 
err),
+                       }
                }
-               newParams[i] = newParam
+               newargs[i] = newarg
        }
-       newInvocation := invocation2.NewRPCInvocation(methodName, newParams, 
invocation.Attachments())
-       newInvocation.SetReply(invocation.Reply())
-       return invoker.Invoke(ctx, newInvocation)
+
+       // build a normal invocation
+       newivc := invocation2.NewRPCInvocation(mtdname, newargs, 
invocation.Attachments())
+       newivc.SetReply(invocation.Reply())
+
+       return invoker.Invoke(ctx, newivc)
 }
 
 // nolint
 func (ef *GenericServiceFilter) OnResponse(ctx context.Context, result 
protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) 
protocol.Result {
        if invocation.MethodName() == constant.GENERIC && 
len(invocation.Arguments()) == 3 && result.Result() != nil {
-               v := reflect.ValueOf(result.Result())
-               if v.Kind() == reflect.Ptr {
-                       v = v.Elem()
+               // get generic info from attachments of invocation, the default 
value is "true"
+               //generic := invocation.AttachmentsByKey(constant.GENERIC_KEY, 
constant.GenericSerializationDefault)
+               // get generalizer according to value in the `generic`
+               g := GetMapGeneralizer()
+
+               obj, err := g.Generalize(result.Result())
+               if err != nil {
+                       err = perrors.Errorf("generalizaion failed, %v", err)
+                       result.SetError(err)
+                       result.SetResult(nil)
+                       return result
                }
-               result.SetResult(objToMap(v.Interface()))
+               result.SetResult(obj)
        }
        return result
 }
diff --git a/filter/filter_impl/generic_service_filter_test.go 
b/filter/filter_impl/generic_service_filter_test.go
index c755a2d..d32e803 100644
--- a/filter/filter_impl/generic_service_filter_test.go
+++ b/filter/filter_impl/generic_service_filter_test.go
@@ -19,131 +19,157 @@ package filter_impl
 
 import (
        "context"
-       "errors"
-       "reflect"
+       "fmt"
+       "net/url"
        "testing"
 )
 
 import (
        hessian "github.com/apache/dubbo-go-hessian2"
+       "github.com/golang/mock/gomock"
        "github.com/stretchr/testify/assert"
 )
 
 import (
        "github.com/apache/dubbo-go/common"
-       "github.com/apache/dubbo-go/common/proxy/proxy_factory"
+       "github.com/apache/dubbo-go/common/constant"
        "github.com/apache/dubbo-go/protocol"
        "github.com/apache/dubbo-go/protocol/invocation"
+       "github.com/apache/dubbo-go/protocol/mock"
 )
 
-type TestStruct struct {
-       AaAa string
-       BaBa string `m:"baBa"`
-       XxYy struct {
-               xxXx string `m:"xxXx"`
-               Xx   string `m:"xx"`
-       } `m:"xxYy"`
-}
+type MockHelloService struct{}
 
-func (c *TestStruct) JavaClassName() string {
-       return "com.test.testStruct"
+func (s *MockHelloService) Hello(who string) (string, error) {
+       return fmt.Sprintf("hello, %s", who), nil
 }
 
-type TestService struct{}
-
-// nolint
-func (ts *TestService) MethodOne(_ context.Context, test1 *TestStruct, test2 
[]TestStruct,
-       test3 interface{}, test4 []interface{}, test5 *string) (*TestStruct, 
error) {
-       if test1 == nil {
-               return nil, errors.New("param test1 is nil")
-       }
-       if test2 == nil {
-               return nil, errors.New("param test2 is nil")
-       }
-       if test3 == nil {
-               return nil, errors.New("param test3 is nil")
-       }
-       if test4 == nil {
-               return nil, errors.New("param test4 is nil")
-       }
-       if test5 == nil {
-               return nil, errors.New("param test5 is nil")
-       }
-       return &TestStruct{}, nil
+func (s *MockHelloService) JavaClassName() string {
+       return "org.apache.dubbo.hello"
 }
 
-// nolint
-func (*TestService) Reference() string {
-       return "com.test.Path"
+func (s *MockHelloService) Reference() string {
+       return "org.apache.dubbo.test"
 }
 
-func TestGenericServiceFilterInvoke(t *testing.T) {
-       hessian.RegisterPOJO(&TestStruct{})
-       methodName := "$invoke"
-       m := make(map[string]interface{})
-       m["AaAa"] = "nihao"
-       x := make(map[string]interface{})
-       x["xxXX"] = "nihaoxxx"
-       m["XxYy"] = x
-       aurguments := []interface{}{
-               "MethodOne",
-               nil,
-               []hessian.Object{
-                       hessian.Object(m),
-                       hessian.Object(append(make([]map[string]interface{}, 
1), m)),
-                       hessian.Object("111"),
-                       hessian.Object(append(make([]map[string]interface{}, 
1), m)),
-                       hessian.Object("222")},
-       }
-       s := &TestService{}
-       _, _ = common.ServiceMap.Register("com.test.Path", "testprotocol", "", 
"", s)
-       rpcInvocation := invocation.NewRPCInvocation(methodName, aurguments, 
nil)
-       filter := GetGenericServiceFilter()
-       url, _ := common.NewURL("testprotocol://127.0.0.1:20000/com.test.Path")
-       result := filter.Invoke(context.Background(), 
&proxy_factory.ProxyInvoker{BaseInvoker: *protocol.NewBaseInvoker(url)}, 
rpcInvocation)
-       assert.NotNil(t, result)
+func TestServiceFilter_Invoke(t *testing.T) {
+       filter := &GenericServiceFilter{}
+
+       ctrl := gomock.NewController(t)
+       defer ctrl.Finish()
+
+       mockInvoker := mock.NewMockInvoker(ctrl)
+
+       // methodName is not "$invoke"
+       invocation1 := invocation.NewRPCInvocation("test", nil, nil)
+       mockInvoker.EXPECT().Invoke(gomock.Eq(invocation1))
+       _ = filter.Invoke(context.Background(), mockInvoker, invocation1)
+       // arguments are nil
+       invocation2 := invocation.NewRPCInvocation(constant.GENERIC, nil, nil)
+       mockInvoker.EXPECT().Invoke(gomock.Eq(invocation2))
+       _ = filter.Invoke(context.Background(), mockInvoker, invocation2)
+       // the number of arguments is not 3
+       invocation3 := invocation.NewRPCInvocation(constant.GENERIC, 
[]interface{}{"hello"}, nil)
+       mockInvoker.EXPECT().Invoke(gomock.Eq(invocation3))
+       _ = filter.Invoke(context.Background(), mockInvoker, invocation3)
+
+       // hello service
+       service := &MockHelloService{}
+       // invoke URL
+       ivkUrl := common.NewURLWithOptions(
+               common.WithProtocol("test"),
+               common.WithParams(url.Values{}),
+               common.WithParamsValue(constant.INTERFACE_KEY, 
service.Reference()),
+               common.WithParamsValue(constant.GENERIC_KEY, 
GENERIC_SERIALIZATION_DEFAULT))
+       // registry RPC service
+       _, err := 
common.ServiceMap.Register(ivkUrl.GetParam(constant.INTERFACE_KEY, ""),
+               ivkUrl.Protocol,
+               "",
+               "",
+               service)
+       assert.Nil(t, err)
+
+       // mock
+       mockInvoker.EXPECT().GetURL().Return(ivkUrl).Times(3)
+
+       // invoke a method without errors using default generalization
+       invocation4 := invocation.NewRPCInvocation(constant.GENERIC,
+               []interface{}{
+                       "Hello",
+                       []string{"java.lang.String"},
+                       []hessian.Object{"world"},
+               }, map[string]interface{}{
+                       constant.GENERIC_KEY: "true",
+               })
+       // invoke a non-existed method
+       invocation5 := invocation.NewRPCInvocation(constant.GENERIC,
+               []interface{}{
+                       "hello11",
+                       []string{"java.lang.String"},
+                       []hessian.Object{"world"},
+               }, map[string]interface{}{
+                       constant.GENERIC_KEY: "true",
+               })
+       // invoke a method with incorrect arguments
+       invocation6 := invocation.NewRPCInvocation(constant.GENERIC,
+               []interface{}{
+                       "Hello",
+                       []string{"java.lang.String", "java.lang.String"},
+                       []hessian.Object{"world", "haha"},
+               }, map[string]interface{}{
+                       constant.GENERIC_KEY: "true",
+               })
+
+       mockInvoker.EXPECT().Invoke(gomock.All(
+               gomock.Not(invocation1),
+               gomock.Not(invocation2),
+               gomock.Not(invocation3),
+       )).DoAndReturn(
+               func(invocation protocol.Invocation) protocol.Result {
+                       switch invocation.MethodName() {
+                       case "Hello":
+                               who := invocation.Arguments()[0].(string)
+                               result, _ := service.Hello(who)
+                               return &protocol.RPCResult{
+                                       Rest: result,
+                               }
+                       default:
+                               panic("this branch shouldn't be reached")
+                       }
+               }).AnyTimes()
+
+       result := filter.Invoke(context.Background(), mockInvoker, invocation4)
        assert.Nil(t, result.Error())
-}
+       assert.Equal(t, "hello, world", result.Result())
 
-func TestGenericServiceFilterResponseTestStruct(t *testing.T) {
-       ts := &TestStruct{
-               AaAa: "aaa",
-               BaBa: "bbb",
-               XxYy: struct {
-                       xxXx string `m:"xxXx"`
-                       Xx   string `m:"xx"`
-               }{},
-       }
-       result := &protocol.RPCResult{
-               Rest: ts,
-       }
-       aurguments := []interface{}{
-               "MethodOne",
-               nil,
-               []hessian.Object{nil},
-       }
-       filter := GetGenericServiceFilter()
-       methodName := "$invoke"
-       rpcInvocation := invocation.NewRPCInvocation(methodName, aurguments, 
nil)
-       r := filter.OnResponse(context.TODO(), result, nil, rpcInvocation)
-       assert.NotNil(t, r.Result())
-       assert.Equal(t, reflect.ValueOf(r.Result()).Kind(), reflect.Map)
+       result = filter.Invoke(context.Background(), mockInvoker, invocation5)
+       assert.Equal(t,
+               fmt.Sprintf("\"hello11\" method is not found, service key: %s", 
ivkUrl.ServiceKey()),
+               fmt.Sprintf("%v", result.Error().(error)))
+
+       result = filter.Invoke(context.Background(), mockInvoker, invocation6)
+       assert.Equal(t,
+               "the number of args(=2) is not matched with \"Hello\" method",
+               fmt.Sprintf("%v", result.Error().(error)))
 }
 
-func TestGenericServiceFilterResponseString(t *testing.T) {
-       str := "111"
-       result := &protocol.RPCResult{
-               Rest: str,
-       }
-       aurguments := []interface{}{
-               "MethodOne",
-               nil,
-               []hessian.Object{nil},
+func TestServiceFilter_OnResponse(t *testing.T) {
+       filter := &GenericServiceFilter{}
+
+       // invoke a method without errors
+       invocation1 := invocation.NewRPCInvocation(constant.GENERIC,
+               []interface{}{
+                       "hello",
+                       []interface{}{"java.lang.String"},
+                       []interface{}{"world"},
+               }, map[string]interface{}{
+                       constant.GENERIC_KEY: "true",
+               })
+
+       rpcResult := &protocol.RPCResult{
+               Rest: "result",
        }
-       filter := GetGenericServiceFilter()
-       methodName := "$invoke"
-       rpcInvocation := invocation.NewRPCInvocation(methodName, aurguments, 
nil)
-       r := filter.OnResponse(context.TODO(), result, nil, rpcInvocation)
-       assert.NotNil(t, r.Result())
-       assert.Equal(t, reflect.ValueOf(r.Result()).Kind(), reflect.String)
+
+       result := filter.OnResponse(context.Background(), rpcResult, nil, 
invocation1)
+       assert.Equal(t, "result", result.Result())
 }
diff --git a/protocol/dubbo/hessian2/hessian_request.go 
b/protocol/dubbo/hessian2/hessian_request.go
index 94aa34d..b281273 100644
--- a/protocol/dubbo/hessian2/hessian_request.go
+++ b/protocol/dubbo/hessian2/hessian_request.go
@@ -19,7 +19,6 @@ package hessian2
 
 import (
        "encoding/binary"
-       "reflect"
        "strconv"
        "strings"
        "time"
@@ -39,95 +38,7 @@ import (
 /////////////////////////////////////////
 
 func getArgType(v interface{}) string {
-       if v == nil {
-               return "V"
-       }
-
-       switch v := v.(type) {
-       // Serialized tags for base types
-       case nil:
-               return "V"
-       case bool:
-               return "Z"
-       case []bool:
-               return "[Z"
-       case byte:
-               return "B"
-       case []byte:
-               return "[B"
-       case int8:
-               return "B"
-       case []int8:
-               return "[B"
-       case int16:
-               return "S"
-       case []int16:
-               return "[S"
-       case uint16: // Equivalent to Char of Java
-               return "C"
-       case []uint16:
-               return "[C"
-       // case rune:
-       //      return "C"
-       case int:
-               return "J"
-       case []int:
-               return "[J"
-       case int32:
-               return "I"
-       case []int32:
-               return "[I"
-       case int64:
-               return "J"
-       case []int64:
-               return "[J"
-       case time.Time:
-               return "java.util.Date"
-       case []time.Time:
-               return "[Ljava.util.Date"
-       case float32:
-               return "F"
-       case []float32:
-               return "[F"
-       case float64:
-               return "D"
-       case []float64:
-               return "[D"
-       case string:
-               return "java.lang.String"
-       case []string:
-               return "[Ljava.lang.String;"
-       case []hessian.Object:
-               return "[Ljava.lang.Object;"
-       case map[interface{}]interface{}:
-               // return  "java.util.HashMap"
-               return "java.util.Map"
-       case hessian.POJOEnum:
-               return v.(hessian.POJOEnum).JavaClassName()
-       //  Serialized tags for complex types
-       default:
-               t := reflect.TypeOf(v)
-               if reflect.Ptr == t.Kind() {
-                       t = reflect.TypeOf(reflect.ValueOf(v).Elem())
-               }
-               switch t.Kind() {
-               case reflect.Struct:
-                       return "java.lang.Object"
-               case reflect.Slice, reflect.Array:
-                       if t.Elem().Kind() == reflect.Struct {
-                               return "[Ljava.lang.Object;"
-                       }
-                       // return "java.util.ArrayList"
-                       return "java.util.List"
-               case reflect.Map: // Enter here, map may be map[string]int
-                       return "java.util.Map"
-               default:
-                       return ""
-               }
-       }
-
-       // unreachable
-       // return "java.lang.RuntimeException"
+       return GetClassDesc(v)
 }
 
 func getArgsTypeList(args []interface{}) (string, error) {
diff --git a/protocol/dubbo/hessian2/java_class.go 
b/protocol/dubbo/hessian2/java_class.go
new file mode 100644
index 0000000..781ff21
--- /dev/null
+++ b/protocol/dubbo/hessian2/java_class.go
@@ -0,0 +1,200 @@
+/*
+ * 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 hessian2
+
+import (
+       "fmt"
+       "reflect"
+       "strings"
+       "time"
+)
+
+import (
+       hessian "github.com/apache/dubbo-go-hessian2"
+       perrors "github.com/pkg/errors"
+)
+
+var (
+       NilError            = perrors.Errorf("object should not be nil")
+       UnexpectedTypeError = perrors.Errorf("object should be a POJO")
+       notBasicClassError  = perrors.Errorf("object isn't a basic class")
+)
+
+// GetJavaName returns java name of an object
+func GetJavaName(obj interface{}) (string, error) {
+       if obj == nil {
+               return "", NilError
+       }
+
+       t := reflect.TypeOf(obj)
+
+       // basic types, e.g. bool, int, etc.
+       if jtype, err := getBasicJavaName(t); err == nil {
+               return jtype, nil
+       }
+
+       // complicated types, e.g. array, slice, etc.
+       switch t.Kind() {
+       case reflect.Array, reflect.Slice:
+               sb := &strings.Builder{}
+               itemtyp := t
+               for itemtyp.Kind() == reflect.Array || itemtyp.Kind() == 
reflect.Slice {
+                       sb.WriteString("[]")
+                       itemtyp = itemtyp.Elem()
+               }
+               var (
+                       javaName string
+                       err      error
+               )
+               if javaName, err = getBasicJavaName(itemtyp); err != nil {
+                       if javaName, err = 
GetJavaName(reflect.New(itemtyp).Elem().Interface()); err != nil {
+                               return "", err
+                       }
+               }
+               return fmt.Sprintf("%s%s", javaName, sb), nil
+       case reflect.Map:
+               return "java.util.Map", nil
+       default:
+               pojo, ok := obj.(hessian.POJO)
+               if !ok {
+                       return "", UnexpectedTypeError
+               }
+               return pojo.JavaClassName(), nil
+       }
+}
+
+func getBasicJavaName(typ reflect.Type) (string, error) {
+       switch typ.Kind() {
+       case reflect.Bool:
+               return "boolean", nil
+       case reflect.Int, reflect.Int64: // in 64-bit processor, Int takes a 
64-bit space
+               return "long", nil
+       case reflect.Int32:
+               return "int", nil
+       case reflect.Int8, reflect.Int16:
+               return "short", nil
+       case reflect.Uint, reflect.Uint64: // in 64-bit processor, Uint takes a 
64-bit space
+               return "unsigned long", nil
+       case reflect.Uint32:
+               return "unsigned int", nil
+       case reflect.Uint16:
+               return "unsigned short", nil
+       case reflect.Uint8:
+               return "char", nil
+       case reflect.Float32:
+               return "float", nil
+       case reflect.Float64:
+               return "double", nil
+       case reflect.String:
+               return "java.lang.String", nil
+       }
+
+       return "", notBasicClassError
+}
+
+// GetClassDesc get class desc.
+// - boolean[].class => "[Z"
+// - Object.class => "Ljava/lang/Object;"
+func GetClassDesc(v interface{}) string {
+       if v == nil {
+               return "V"
+       }
+
+       switch v := v.(type) {
+       // Serialized tags for base types
+       case nil:
+               return "V"
+       case bool:
+               return "Z"
+       case []bool:
+               return "[Z"
+       case byte:
+               return "B"
+       case []byte:
+               return "[B"
+       case int8:
+               return "B"
+       case []int8:
+               return "[B"
+       case int16:
+               return "S"
+       case []int16:
+               return "[S"
+       case uint16: // Equivalent to Char of Java
+               return "C"
+       case []uint16:
+               return "[C"
+       // case rune:
+       //      return "C"
+       case int:
+               return "J"
+       case []int:
+               return "[J"
+       case int32:
+               return "I"
+       case []int32:
+               return "[I"
+       case int64:
+               return "J"
+       case []int64:
+               return "[J"
+       case time.Time:
+               return "java.util.Date"
+       case []time.Time:
+               return "[Ljava.util.Date"
+       case float32:
+               return "F"
+       case []float32:
+               return "[F"
+       case float64:
+               return "D"
+       case []float64:
+               return "[D"
+       case string:
+               return "java.lang.String"
+       case []string:
+               return "[Ljava.lang.String;"
+       case []hessian.Object:
+               return "[Ljava.lang.Object;"
+       case map[interface{}]interface{}:
+               // return  "java.util.HashMap"
+               return "java.util.Map"
+       case hessian.POJOEnum:
+               return v.(hessian.POJOEnum).JavaClassName()
+       //  Serialized tags for complex types
+       default:
+               t := reflect.TypeOf(v)
+               if reflect.Ptr == t.Kind() {
+                       t = reflect.TypeOf(reflect.ValueOf(v).Elem())
+               }
+               switch t.Kind() {
+               case reflect.Struct:
+                       return "java.lang.Object"
+               case reflect.Slice, reflect.Array:
+                       if t.Elem().Kind() == reflect.Struct {
+                               return "[Ljava.lang.Object;"
+                       }
+                       // return "java.util.ArrayList"
+                       return "java.util.List"
+               case reflect.Map: // Enter here, map may be map[string]int
+                       return "java.util.Map"
+               default:
+                       return ""
+               }
+       }
+}
diff --git a/protocol/dubbo/hessian2/java_class_test.go 
b/protocol/dubbo/hessian2/java_class_test.go
new file mode 100644
index 0000000..246f139
--- /dev/null
+++ b/protocol/dubbo/hessian2/java_class_test.go
@@ -0,0 +1,132 @@
+/*
+ * 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 hessian2
+
+import (
+       "testing"
+)
+
+import (
+       "github.com/stretchr/testify/assert"
+)
+
+func TestGetJavaName(t *testing.T) {
+       _, err := GetJavaName(nil)
+       assert.Equal(t, NilError, err)
+
+       typ, err := GetJavaName(true)
+       assert.Equal(t, "boolean", typ)
+       assert.Nil(t, err)
+
+       typ, err = GetJavaName(1)
+       assert.Equal(t, "long", typ)
+       assert.Nil(t, err)
+       typ, err = GetJavaName(int64(1))
+       assert.Equal(t, "long", typ)
+       assert.Nil(t, err)
+
+       typ, err = GetJavaName(int32(1))
+       assert.Equal(t, "int", typ)
+       assert.Nil(t, err)
+
+       typ, err = GetJavaName(int16(1))
+       assert.Equal(t, "short", typ)
+       assert.Nil(t, err)
+       typ, err = GetJavaName(int8(1))
+       assert.Equal(t, "short", typ)
+       assert.Nil(t, err)
+
+       typ, err = GetJavaName(uint(1))
+       assert.Equal(t, "unsigned long", typ)
+       assert.Nil(t, err)
+       typ, err = GetJavaName(uint64(1))
+       assert.Equal(t, "unsigned long", typ)
+       assert.Nil(t, err)
+
+       typ, err = GetJavaName(uint32(1))
+       assert.Equal(t, "unsigned int", typ)
+       assert.Nil(t, err)
+
+       typ, err = GetJavaName(uint16(1))
+       assert.Equal(t, "unsigned short", typ)
+       assert.Nil(t, err)
+       typ, err = GetJavaName(byte('a'))
+       assert.Equal(t, "char", typ)
+       assert.Nil(t, err)
+
+       typ, err = GetJavaName(float32(1.0))
+       assert.Equal(t, "float", typ)
+       assert.Nil(t, err)
+
+       typ, err = GetJavaName(1.0)
+       assert.Equal(t, "double", typ)
+       assert.Nil(t, err)
+
+       typ, err = GetJavaName("hello")
+       assert.Equal(t, "java.lang.String", typ)
+       assert.Nil(t, err)
+
+       typ, err = GetJavaName([]string{"hello"})
+       assert.Equal(t, "java.lang.String[]", typ)
+       assert.Nil(t, err)
+       typ, err = GetJavaName([]*mockPOJOPtr{{}})
+       assert.Equal(t, "org.apache.dubbo.mockPOJOPtr[]", typ)
+       assert.Nil(t, err)
+       typ, err = GetJavaName([]mockPOJO{{}})
+       assert.Equal(t, "org.apache.dubbo.mockPOJO[]", typ)
+       assert.Nil(t, err)
+       typ, err = GetJavaName([][]string{{"hello"}})
+       assert.Equal(t, "java.lang.String[][]", typ)
+       assert.Nil(t, err)
+       typ, err = GetJavaName([][]*mockPOJOPtr{{&mockPOJOPtr{}}})
+       assert.Equal(t, "org.apache.dubbo.mockPOJOPtr[][]", typ)
+       assert.Nil(t, err)
+       typ, err = GetJavaName([1]string{"hello"})
+       assert.Equal(t, "java.lang.String[]", typ)
+       assert.Nil(t, err)
+
+       typ, err = GetJavaName(map[string]string{"key1": "value1"})
+       assert.Equal(t, "java.util.Map", typ)
+       assert.Nil(t, err)
+
+       typ, err = GetJavaName(mockPOJO{})
+       assert.Equal(t, "org.apache.dubbo.mockPOJO", typ)
+       assert.Nil(t, err)
+       typ, err = GetJavaName(&mockPOJOPtr{})
+       assert.Equal(t, "org.apache.dubbo.mockPOJOPtr", typ)
+       assert.Nil(t, err)
+
+       _, err = GetJavaName(&mockNonPOJO{})
+       assert.Equal(t, UnexpectedTypeError, err)
+}
+
+type mockPOJOPtr struct {
+       TestField string
+}
+
+func (m *mockPOJOPtr) JavaClassName() string {
+       return "org.apache.dubbo.mockPOJOPtr"
+}
+
+type mockPOJO struct{}
+
+func (m mockPOJO) JavaClassName() string {
+       return "org.apache.dubbo.mockPOJO"
+}
+
+type mockNonPOJO struct{}

Reply via email to