blob: b686d5728d026fdb1e37180d5a9d24d32bf4b7bc [file] [log] [blame]
/*
* 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 (
"math"
"reflect"
"strconv"
"strings"
"time"
)
import (
hessian "github.com/apache/dubbo-go-hessian2"
"github.com/apache/dubbo-go-hessian2/java_exception"
perrors "github.com/pkg/errors"
)
import (
"github.com/apache/dubbo-go/common"
"github.com/apache/dubbo-go/common/constant"
"github.com/apache/dubbo-go/common/logger"
)
type Object interface{}
type HessianSerializer struct {
}
func (h HessianSerializer) Marshal(p DubboPackage) ([]byte, error) {
encoder := hessian.NewEncoder()
if p.IsRequest() {
return marshalRequest(encoder, p)
}
return marshalResponse(encoder, p)
}
func (h HessianSerializer) Unmarshal(input []byte, p *DubboPackage) error {
if p.IsHeartBeat() {
return nil
}
if p.IsRequest() {
return unmarshalRequestBody(input, p)
}
return unmarshalResponseBody(input, p)
}
func marshalResponse(encoder *hessian.Encoder, p DubboPackage) ([]byte, error) {
header := p.Header
response := EnsureResponsePayload(p.Body)
if header.ResponseStatus == Response_OK {
if p.IsHeartBeat() {
encoder.Encode(nil)
} else {
var version string
if attachmentVersion, ok := response.Attachments[DUBBO_VERSION_KEY]; ok {
version = attachmentVersion.(string)
}
atta := isSupportResponseAttachment(version)
var resWithException, resValue, resNullValue int32
if atta {
resWithException = RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS
resValue = RESPONSE_VALUE_WITH_ATTACHMENTS
resNullValue = RESPONSE_NULL_VALUE_WITH_ATTACHMENTS
} else {
resWithException = RESPONSE_WITH_EXCEPTION
resValue = RESPONSE_VALUE
resNullValue = RESPONSE_NULL_VALUE
}
if response.Exception != nil { // throw error
encoder.Encode(resWithException)
if t, ok := response.Exception.(java_exception.Throwabler); ok {
encoder.Encode(t)
} else {
encoder.Encode(java_exception.NewThrowable(response.Exception.Error()))
}
} else {
if response.RspObj == nil {
encoder.Encode(resNullValue)
} else {
encoder.Encode(resValue)
encoder.Encode(response.RspObj) // result
}
}
if atta {
encoder.Encode(response.Attachments) // attachments
}
}
} else {
if response.Exception != nil { // throw error
encoder.Encode(response.Exception.Error())
} else {
encoder.Encode(response.RspObj)
}
}
bs := encoder.Buffer()
// encNull
bs = append(bs, byte('N'))
return bs, nil
}
func marshalRequest(encoder *hessian.Encoder, p DubboPackage) ([]byte, error) {
service := p.Service
request := EnsureRequestPayload(p.Body)
encoder.Encode(DEFAULT_DUBBO_PROTOCOL_VERSION)
encoder.Encode(service.Path)
encoder.Encode(service.Version)
encoder.Encode(service.Method)
args, ok := request.Params.([]interface{})
if !ok {
logger.Infof("request args are: %+v", request.Params)
return nil, perrors.Errorf("@params is not of type: []interface{}")
}
types, err := getArgsTypeList(args)
if err != nil {
return nil, perrors.Wrapf(err, " PackRequest(args:%+v)", args)
}
encoder.Encode(types)
for _, v := range args {
encoder.Encode(v)
}
request.Attachments[PATH_KEY] = service.Path
request.Attachments[VERSION_KEY] = service.Version
if len(service.Group) > 0 {
request.Attachments[GROUP_KEY] = service.Group
}
if len(service.Interface) > 0 {
request.Attachments[INTERFACE_KEY] = service.Interface
}
if service.Timeout != 0 {
request.Attachments[TIMEOUT_KEY] = strconv.Itoa(int(service.Timeout / time.Millisecond))
}
encoder.Encode(request.Attachments)
return encoder.Buffer(), nil
}
var versionInt = make(map[string]int)
// https://github.com/apache/dubbo/blob/dubbo-2.7.1/dubbo-common/src/main/java/org/apache/dubbo/common/Version.java#L96
// isSupportResponseAttachment is for compatibility among some dubbo version
func isSupportResponseAttachment(version string) bool {
if version == "" {
return false
}
v, ok := versionInt[version]
if !ok {
v = version2Int(version)
if v == -1 {
return false
}
}
if v >= 2001000 && v <= 2060200 { // 2.0.10 ~ 2.6.2
return false
}
return v >= LOWEST_VERSION_FOR_RESPONSE_ATTACHMENT
}
func version2Int(version string) int {
var v = 0
varr := strings.Split(version, ".")
length := len(varr)
for key, value := range varr {
v0, err := strconv.Atoi(value)
if err != nil {
return -1
}
v += v0 * int(math.Pow10((length-key-1)*2))
}
if length == 3 {
return v * 100
}
return v
}
func unmarshalRequestBody(body []byte, p *DubboPackage) error {
if p.Body == nil {
p.SetBody(make([]interface{}, 7))
}
decoder := hessian.NewDecoder(body)
var (
err error
dubboVersion, target, serviceVersion, method, argsTypes interface{}
args []interface{}
)
req, ok := p.Body.([]interface{})
if !ok {
return perrors.Errorf("@reqObj is not of type: []interface{}")
}
dubboVersion, err = decoder.Decode()
if err != nil {
return perrors.WithStack(err)
}
req[0] = dubboVersion
target, err = decoder.Decode()
if err != nil {
return perrors.WithStack(err)
}
req[1] = target
serviceVersion, err = decoder.Decode()
if err != nil {
return perrors.WithStack(err)
}
req[2] = serviceVersion
method, err = decoder.Decode()
if err != nil {
return perrors.WithStack(err)
}
req[3] = method
argsTypes, err = decoder.Decode()
if err != nil {
return perrors.WithStack(err)
}
req[4] = argsTypes
ats := hessian.DescRegex.FindAllString(argsTypes.(string), -1)
var arg interface{}
for i := 0; i < len(ats); i++ {
arg, err = decoder.Decode()
if err != nil {
return perrors.WithStack(err)
}
args = append(args, arg)
}
req[5] = args
attachments, err := decoder.Decode()
if err != nil {
return perrors.WithStack(err)
}
if v, ok := attachments.(map[interface{}]interface{}); ok {
v[DUBBO_VERSION_KEY] = dubboVersion
req[6] = ToMapStringInterface(v)
buildServerSidePackageBody(p)
return nil
}
return perrors.Errorf("get wrong attachments: %+v", attachments)
}
func unmarshalResponseBody(body []byte, p *DubboPackage) error {
decoder := hessian.NewDecoder(body)
rspType, err := decoder.Decode()
if p.Body == nil {
p.SetBody(&ResponsePayload{})
}
if err != nil {
return perrors.WithStack(err)
}
response := EnsureResponsePayload(p.Body)
switch rspType {
case RESPONSE_WITH_EXCEPTION, RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS:
expt, err := decoder.Decode()
if err != nil {
return perrors.WithStack(err)
}
if rspType == RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS {
attachments, err := decoder.Decode()
if err != nil {
return perrors.WithStack(err)
}
if v, ok := attachments.(map[interface{}]interface{}); ok {
atta := ToMapStringInterface(v)
response.Attachments = atta
} else {
return perrors.Errorf("get wrong attachments: %+v", attachments)
}
}
if e, ok := expt.(error); ok {
response.Exception = e
} else {
response.Exception = perrors.Errorf("got exception: %+v", expt)
}
return nil
case RESPONSE_VALUE, RESPONSE_VALUE_WITH_ATTACHMENTS:
rsp, err := decoder.Decode()
if err != nil {
return perrors.WithStack(err)
}
if rspType == RESPONSE_VALUE_WITH_ATTACHMENTS {
attachments, err := decoder.Decode()
if err != nil {
return perrors.WithStack(err)
}
if v, ok := attachments.(map[interface{}]interface{}); ok {
atta := ToMapStringInterface(v)
response.Attachments = atta
} else {
return perrors.Errorf("get wrong attachments: %+v", attachments)
}
}
return perrors.WithStack(hessian.ReflectResponse(rsp, response.RspObj))
case RESPONSE_NULL_VALUE, RESPONSE_NULL_VALUE_WITH_ATTACHMENTS:
if rspType == RESPONSE_NULL_VALUE_WITH_ATTACHMENTS {
attachments, err := decoder.Decode()
if err != nil {
return perrors.WithStack(err)
}
if v, ok := attachments.(map[interface{}]interface{}); ok {
atta := ToMapStringInterface(v)
response.Attachments = atta
} else {
return perrors.Errorf("get wrong attachments: %+v", attachments)
}
}
return nil
}
return nil
}
func buildServerSidePackageBody(pkg *DubboPackage) {
req := pkg.GetBody().([]interface{}) // length of body should be 7
if len(req) > 0 {
var dubboVersion, argsTypes string
var args []interface{}
var attachments map[string]interface{}
svc := Service{}
if req[0] != nil {
dubboVersion = req[0].(string)
}
if req[1] != nil {
svc.Path = req[1].(string)
}
if req[2] != nil {
svc.Version = req[2].(string)
}
if req[3] != nil {
svc.Method = req[3].(string)
}
if req[4] != nil {
argsTypes = req[4].(string)
}
if req[5] != nil {
args = req[5].([]interface{})
}
if req[6] != nil {
attachments = req[6].(map[string]interface{})
}
if svc.Path == "" && attachments[constant.PATH_KEY] != nil && len(attachments[constant.PATH_KEY].(string)) > 0 {
svc.Path = attachments[constant.PATH_KEY].(string)
}
if _, ok := attachments[constant.INTERFACE_KEY]; ok {
svc.Interface = attachments[constant.INTERFACE_KEY].(string)
} else {
svc.Interface = svc.Path
}
if _, ok := attachments[constant.GROUP_KEY]; ok {
svc.Group = attachments[constant.GROUP_KEY].(string)
}
pkg.SetService(svc)
pkg.SetBody(map[string]interface{}{
"dubboVersion": dubboVersion,
"argsTypes": argsTypes,
"args": args,
"service": common.ServiceMap.GetService(DUBBO, svc.Path), // path as a key
"attachments": attachments,
})
}
}
func getArgsTypeList(args []interface{}) (string, error) {
var (
typ string
types string
)
for i := range args {
typ = getArgType(args[i])
if typ == "" {
return types, perrors.Errorf("cat not get arg %#v type", args[i])
}
if !strings.Contains(typ, ".") {
types += typ
} else if strings.Index(typ, "[") == 0 {
types += strings.Replace(typ, ".", "/", -1)
} else {
// java.util.List -> Ljava/util/List;
types += "L" + strings.Replace(typ, ".", "/", -1) + ";"
}
}
return types, nil
}
func getArgType(v interface{}) string {
if v == nil {
return "V"
}
switch 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 []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"
}
func ToMapStringInterface(origin map[interface{}]interface{}) map[string]interface{} {
dest := make(map[string]interface{}, len(origin))
for k, v := range origin {
if kv, ok := k.(string); ok {
if v == nil {
dest[kv] = ""
continue
}
dest[kv] = v
}
}
return dest
}
func init() {
SetSerializer("hessian2", HessianSerializer{})
}