Git Product home page Git Product logo

apache / dubbo-go-hessian2 Goto Github PK

View Code? Open in Web Editor NEW
207.0 52.0 112.0 766 KB

caucho hessian2 implementation in Go for [apache/dubbo-go](https://github.com/apache/dubbo-go) which is compatible with [dubbo-hessian-lite](https://github.com/apache/dubbo-hessian-lite)

License: Apache License 2.0

Go 83.94% Java 15.70% Makefile 0.16% Shell 0.20%
dubbogo dubbo-go dubbo dubbox hessian hessian2 caucho apache-dubbo-go dubbo-hessian-lite dubbo-hessian

dubbo-go-hessian2's Issues

Imp: hessian2 should handle all errors of encode and decode

What would you like to be added:
improve hessian2 error handling

Why is this needed:
If you don't handle these errors, it will cause errors to appear in the upper application, will occur some unforeseen, weird problems. Which is hard to trace reason.

eg:
https://github.com/dubbogo/hessian2/blob/c432dcada9eab283abc851c7f65760f18a0377c6/request.go#L172
https://github.com/dubbogo/hessian2/blob/c432dcada9eab283abc851c7f65760f18a0377c6/request.go#L173
https://github.com/dubbogo/hessian2/blob/c432dcada9eab283abc851c7f65760f18a0377c6/request.go#L174
https://github.com/dubbogo/hessian2/blob/c432dcada9eab283abc851c7f65760f18a0377c6/request.go#L175

java Exception class

What would you like to be added:

 Can it support Java exception classes?

Why is this needed:

When dubbo-go's returns an exception, java need decode as Throwable.

java code:

Object obj = in.readObject();
if (obj instanceof Throwable == false)
     throw new IOException("Response data error, expect Throwable, but get " + obj);
setException((Throwable) obj);
setAttachments((Map<String, String>) in.readObject(Map.class));

HessianCodec包头长度检查异常

func (h *HessianCodec) ReadHeader(header *DubboHeader) error {

var err error

if h.reader.Size() < HEADER_LENGTH {
	return ErrHeaderNotEnough
}
buf, err := h.reader.Peek(HEADER_LENGTH)
if err != nil { // this is impossible
	return perrors.WithStack(err)
}
...

这一段会检查长度是否大于包头长度。由下面这段调用。

// Unmarshal ...
func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, opts ...interface{}) error {
codec := hessian.NewHessianCodec(bufio.NewReaderSize(buf, buf.Len()))

// read header
err := codec.ReadHeader(&p.Header)

但是在bufio.NewReaderSize(buf, buf.Len())中

func NewReaderSize(rd io.Reader, size int) *Reader {
// Is it already a Reader?
b, ok := rd.(*Reader)
if ok && len(b.buf) >= size {
return b
}
if size < minReadBufferSize {
size = minReadBufferSize
}
r := new(Reader)
r.reset(make([]byte, size), rd)
return r
}

这里创建出的reader长度永远大于16.导致包头分包时,解包异常。

StringBuilder & StringBuffer 使用string接收会报错

**err info**
2019-07-20T14:57:43.620+0800	WARN	{client:TCP_CLIENT:2:10.0.75.1:51904<->10.0.75.1:20880}, [session.handleTCPPackage] = len{0}, error:unknown string tag 0x43

java-server

  • WrapUser
    private String str1;
    private StringBuilder strBuilder2; //test
    private StringBuffer strBuffer3; //test
    private BigDecimal bigdecimalNum4;
    private Date time5;
	public WrapUser WrapTypeInspect() { 
		// WrapUser(String id, BigDecimal fracNum, Integer age, Date time)
		/*
		 * String id BigDecimal Integer Date
		 */
		WrapUser wrapRes = new WrapUser(null, null, null, null, null);
		wrapRes.setStr1("No.6546432");
		//wrapRes.setStrBuilder2(new StringBuilder("StringBuilder Test"));
		//wrapRes.setStrBuffer3(new StringBuffer("StringBuffer Test"));
		wrapRes.setBigdecimalNum4(new BigDecimal("123.456"));
		wrapRes.setTime5(new Date());
		return wrapRes;
	}

  • 给null值时,string可以正常接收
    response result: {No.6546432 null null {3 3 3 false [123 456000000 0 0 0 0 0 0 0] 123.456} 2019-07-20 15:06:13.364 +0800 CST}
    image

  • 给值时报错
    2019-07-20T15:10:38.257+0800 ERROR pkg.Unmarshal(ss:&{name:client endPoint:0xc000154100 Connection:0xc0003f6000 listener:0xc000340048 reader:0xc000340040 writer:0xc000340040 rQ:0xc00015aae0 wQ:0xc00015ab40 maxMsgLen:10240 tPool: period:5000000000 wait:1000000000 once:{m:{state:0 sema:0} done:0} done:0xc00014a1e0 attrs:0xc00016a4a0 grNum:2 lock:{w:{state:0 sema:0} writerSem:0 readerSem:0 readerCount:0 readerWait:0}}, len(@DaTa):231) = error:unknown string tag 0x43

image

Add: mapping go *bool to java Boolean

What would you like to be added:
mapping go *bool to java Boolean

  • codec support between java Boolean null and go *bool nil

Why is this needed:

  • Boolean is used widely in java

Todo: fix the problems of v1.4

What would you like to be added:

1 the import block of java_exception_test.go should be splitted;
2 pls add remark for Response and EnsureResponse in response.go;

go serialization & java unserialization issue

What happened:

1.serialization failed when I try to unserialize go class with inheritance
2.lose attribute when I try to unserialize java class with implement

What you expected to happen:

Serialize between java and go for all type

How to reproduce it (as minimally and precisely as possible):
Q1.
`package main

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

type BasketballPlayer struct {
Skill string
}

type Demo struct {
BasketballPlayer //不支持
Team string
Name string
Number int
Description []string
Teammate map[string]string
}

func (d Demo) JavaClassName() string {
return "app.pojo.Demo"
}

func main() {
file, err := os.OpenFile("F:\serialization\serializedResult", os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
panic(err)
}
defer file.Close()

t := Demo{
    Team:        "Lakers",
    Name:        "LebornJames",
    Number:      23,
    Description: []string{"61 points", "3 champions"},
    Teammate:    map[string]string{"3": "wade", "2": "irving"},
}
e := hessian.NewEncoder()
err = e.Encode(t)
if err != nil {
    panic(err)
}

_, err = file.Write(e.Buffer())
if err != nil {
    panic(err)
}
fmt.Println("serialize done!")

}
`
I was told "panic: failed to encode field: main.BasketballPlayer, {Skill:}: struct type not Support! main.BasketballPlayer[{}] is not a instance of POJO!" when I try to dubug this

Q2.
java code:
Association.java
`package app.pojo;

import java.io.Serializable;

public interface Association {
String associationName = "NBA";
}
`

BasketballPlayer.java
`package app.pojo;

import java.io.Serializable;

public class BasketballPlayer implements Serializable {
public static final long serialVersionUID = 1L;
private String skill = "jump shot";

}
`

Demo.java
`package app.pojo;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

import lombok.Data;

@DaTa
public class Demo extends BasketballPlayer implements Serializable,Association {
private static final long serialVersionUID = 1L;
public String skill;
private String team;
private String name;
private int number;
private List description;
private Map<String, String> teammate;

@Override
public String toString() {
    return "Demo{" +
            "skill=" + skill +
            "' team='" + team +
            "' name='" + name +
            "', number=" + number +
            "', description='" + description +
            "', teammate='" + teammate +
            "', association='" + associationName +"'}";
}

}
**App.java**package app;

import java.io.ByteArrayOutputStream;

import com.caucho.hessian.io.Hessian2Output;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import app.pojo.Demo;

public class App {
public static void main(String[] args) {
Demo demo = new Demo();
demo.setTeam("Lakers");
demo.setName("kobeBryant");
demo.setNumber(24);
List list = new ArrayList();
list.add("81 points");
list.add("5 champions");
demo.setDescription(list);
Map map = new HashMap();
map.put("34","O'Neal");
map.put("16", "Gasol");
demo.setTeammate(map);
try {
byte[] data = App.serialize(demo);
FileOutputStream os =new FileOutputStream(new File("F:\serialization\serializedResult"));
System.out.println(data.length);
os.write(data);
os.close();
System.out.println("done");
} catch (Exception e) {
e.printStackTrace();
}
}

public static byte[] serialize(Object obj){
    ByteArrayOutputStream byteArrayOutputStream = null;
    Hessian2Output hessianOutput = null;
    try {
        byteArrayOutputStream = new ByteArrayOutputStream();
        // Hessian的序列化输出
        hessianOutput = new Hessian2Output(byteArrayOutputStream);
        hessianOutput.writeObject(obj);
        hessianOutput.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return byteArrayOutputStream.toByteArray();
}

}

`

go code

`package main

import (
"fmt"
"github.com/apache/dubbo-go-hessian2"
"io/ioutil"
"os"
)

type Demo struct {
Skill string
Team string
Name string
Number int
Description []string
Teammate map[string] string
AssociationName string
}

func (d Demo) JavaClassName() string {
return "app.pojo.Demo"
}

func main() {
file, err := os.Open("F:\serialization\serializedResult")
if err != nil {
panic(err)
}
defer file.Close()

t := Demo{}
e := hessian.NewEncoder()
e.Encode(t)
if err != nil {
   panic(err)
}

bytes, _ := ioutil.ReadAll(file)
decodedObject, err := hessian.NewDecoder(bytes).Decode()
if err != nil {
    panic(err)
}

d, ok := decodedObject.(*Demo)
if !ok {
    panic("fail")
}
fmt.Printf("%v\n", *d)

}`

result:

API server listening at: 127.0.0.1:50036
{jump shot Lakers kobeBryant 24 [81 points 5 champions] map[16:Gasol 34:O'Neal] }

it lose association='NBA' (didn't happen when I try to unserialize with java)

Anything else we need to know?:

Any solution to fix that problem

多级指针反序列化bug

What happened:
type Demo{
Name ***string
}
当目标为多级指针时会出现序列化错误,当前已暂时修复部分类型。具体解决方案未出。
What you expected to happen:

How to reproduce it (as minimally and precisely as possible):

Anything else we need to know?:

支持解码dubbo服务调用attachment

背景

在service mesh场景中,使用hessian-go作为序列化框架,需要解析dubbo请求body中的attachment(里面包含group)。因为attachment在body中方法参数字节的后面,所以需要先解析dubbo请求中的参数类型和参数值,然后才能去解析到attachemt。

What happened:

目前发现解析attachment之前,先解析方法的参数类型和参数值报错了。类似参数类型解析不到go对应的类型:can not find go type name com.raycloud.notify.api.domain.StopNotifyJob in registry.

因为请求的参数类型,是java应用发到service mesh的,因此service mesh无法提前注册StopNotifyJob类型,并且也无法知道它的类型。

What you expected to happen:

类似java的hessian序列化行为,如果反序列化类型找不到(比如是class类型),可以降级为hashmap(对应golang里面的map或者sync.Map). 这样参数解析完之后,也能解析到attachment。

How to reproduce it (as minimally and precisely as possible):

这个是真实线上java(dubbo 2.4.9)调用mesh的二进制字节流:

bytes := []byte{
	0xda, 0xbb, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c,
	0x00, 0x00, 0x01, 0xfb, 0x05, 0x32, 0x2e, 0x36, 0x2e, 0x32, 0x30, 0x30, 0x63, 0x6f, 0x6d, 0x2e,
	0x72, 0x61, 0x79, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x2e,
	0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4e, 0x6f, 0x74, 0x69,
	0x66, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x0e, 0x32, 0x2e, 0x30,
	0x2e, 0x31, 0x2d, 0x76, 0x70, 0x63, 0x2d, 0x74, 0x65, 0x73, 0x74, 0x0d, 0x73, 0x74, 0x6f, 0x70,
	0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x4a, 0x6f, 0x62, 0x30, 0x2e, 0x4c, 0x63, 0x6f, 0x6d, 0x2f,
	0x72, 0x61, 0x79, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x2f,
	0x61, 0x70, 0x69, 0x2f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2f, 0x53, 0x74, 0x6f, 0x70, 0x4e,
	0x6f, 0x74, 0x69, 0x66, 0x79, 0x4a, 0x6f, 0x62, 0x3b, 0x43, 0x30, 0x2c, 0x63, 0x6f, 0x6d, 0x2e,
	0x72, 0x61, 0x79, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x2e,
	0x61, 0x70, 0x69, 0x2e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x4e,
	0x6f, 0x74, 0x69, 0x66, 0x79, 0x4a, 0x6f, 0x62, 0x98, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73,
	0x06, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x04, 0x6e, 0x69, 0x63, 0x6b, 0x04, 0x74, 0x79, 0x70,
	0x65, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x06, 0x61, 0x70, 0x70,
	0x4b, 0x65, 0x79, 0x05, 0x61, 0x70, 0x70, 0x49, 0x64, 0x08, 0x74, 0x61, 0x6f, 0x62, 0x61, 0x6f,
	0x49, 0x64, 0x60, 0x01, 0x31, 0x01, 0x31, 0x01, 0x31, 0x01, 0x31, 0x01, 0x31, 0x01, 0x31, 0x91,
	0xe1, 0x48, 0x05, 0x61, 0x2e, 0x73, 0x2e, 0x75, 0x0a, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x2f, 0x74,
	0x65, 0x73, 0x74, 0x05, 0x61, 0x2e, 0x73, 0x2e, 0x74, 0x10, 0x35, 0x33, 0x35, 0x36, 0x32, 0x35,
	0x38, 0x38, 0x39, 0x38, 0x37, 0x37, 0x31, 0x33, 0x31, 0x34, 0x05, 0x61, 0x2e, 0x73, 0x2e, 0x64,
	0x10, 0x6b, 0x65, 0x66, 0x75, 0x2e, 0x6b, 0x75, 0x61, 0x69, 0x6d, 0x61, 0x69, 0x2e, 0x63, 0x6f,
	0x6d, 0x04, 0x70, 0x61, 0x74, 0x68, 0x30, 0x30, 0x63, 0x6f, 0x6d, 0x2e, 0x72, 0x61, 0x79, 0x63,
	0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x2e, 0x61, 0x70, 0x69, 0x2e,
	0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x4a, 0x6f,
	0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x41, 0x70,
	0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x14, 0x63, 0x6f, 0x6d, 0x2d, 0x72, 0x61,
	0x79, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2d, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2d, 0x78, 0x09, 0x69,
	0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x30, 0x30, 0x63, 0x6f, 0x6d, 0x2e, 0x72, 0x61,
	0x79, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x2e, 0x61, 0x70,
	0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79,
	0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69,
	0x6f, 0x6e, 0x0e, 0x32, 0x2e, 0x30, 0x2e, 0x31, 0x2d, 0x76, 0x70, 0x63, 0x2d, 0x74, 0x65, 0x73,
	0x74, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x04, 0x35, 0x30, 0x30, 0x30, 0x5a,

}

在用这个库解码dubbo时,遇到类似以下错误:
image

Anything else we need to know?:

这里附上线上抓包的二进制:

dubbo.pcap.zip

go []string to java []string

What would you like to be added:

image

image

golang数据结构中的[]string被java识别为java.util.ArrayList。后来发现是该版本hessaingolang只支持无类型list。导致图二java抛出异常。

Why is this needed:
实际使用中数据结构不可控,需要支持。以上图二的代码为com.alibaba.dubbo.rpc.filter.GenericFilter的源码,也无法修改。

反序列化出错

当反序列化一个对象的时候,发生错误。但是同样的请求用java反序列化正常。
相关代码

type TriggerParam struct {
	JobId                 int
	ExecutorHandler       string
	ExecutorParams        string
	ExecutorBlockStrategy string
	ExecutorTimeout       int
	LogId                 int
	LogDateTim            int
	GlueType              string
	GlueSource            string
	GlueUpdatetime        int
	BroadcastIndex        int
	BroadcastTotal        int
}

func (TriggerParam) JavaClassName() string {
	return "com.xxl.job.core.biz.model.TriggerParam"
}

type XxlRpcRequest struct {
	RequestId        string
	CreateMillisTime int
	AccessToken      string
	ClassName        string
	MethodName       string
	ParameterTypes   []java_exception.Class
	Parameters       []interface{}
	Version          string
}

func (XxlRpcRequest) JavaClassName() string {
	return "com.xxl.rpc.remoting.net.params.XxlRpcRequest"
}

func TestSerializerFile(t *testing.T) {

	RegisterPOJO(XxlRpcRequest{})
	RegisterPOJO(TriggerParam{})
	f, _ := os.Open("test.dat")
	bt, _ := ioutil.ReadAll(f)
	_ = f.Close()
	for e := range bt {
		fmt.Printf("0x%x ", bt[e])
	}
	fmt.Println()
	fmt.Println(string(bt))
	d := NewDecoder(bt)
	res, err := d.Decode()
	if err != nil {
		t.Errorf("Decode() = %+v", err)
	}
	resJson, _ := json.Marshal(res)
	t.Logf("decode = %v, %v\n", string(resJson), err)
}

二进制文件:https://github.com/sniperking1234/dubbo-go-hessian2/blob/xxl-job/test.dat
解析的时候报错
image
用java反序列化的结果:
image

Fix: bugs of apache/dubbo-hesisan-lite v3.2.7

What happened:
The latest dubbo-hessian-lite has released which fixed some bugs as the following picture.

image

The bug 1,3,4 may be related with dubbo-go-hessian2. If so we should fixed them too.

What you expected to happen:

Hope our big boss @zonghaishang check them exist in dubbo-go-hessian2 or not. If indeed exist, pls fix them.

Add: mapping go *int to java Integer

What would you like to be added:
mapping go *int to java Integer

  • codec support between java Integer null and go *int nil

Why is this needed:

  • Integer is used widely in java

Java Exception class supplement

Explain

  • Your files should be in the java_exception folder, and named in format of [exception name].go. Reference java_exception.go
  • Test should be in the java_exception_test.go. Reference java_exception_test.go
  • Please reply the number to represent your work, thanks.
  • Welcome to add more exceptions to issue.

1

  • RuntimeException
  • IncompleteAnnotationException
  • AnnotationTypeMismatchException
  • NegativeArraySizeException
  • UnsupportedOperationException
  • ArithmeticException

2

  • IllegalMonitorStateException
  • ArrayStoreException
  • IllegalStateException
  • ClassCastException
  • UncheckedIOException
  • EnumConstantNotPresentException

3

  • NullPointerException
  • TypeNotPresentException
  • UndeclaredThrowableException
  • MalformedParametersException
  • WrongMethodTypeException
  • MalformedParameterizedTypeException

4

  • SecurityException
  • IllegalArgumentException
  • IllegalThreadStateException
  • NumberFormatException
  • IndexOutOfBoundsException
  • ArrayIndexOutOfBoundsException
  • StringIndexOutOfBoundsException

5

  • IllegalClassFormatException
  • ReflectiveOperationException
  • InvocationTargetException
  • NoSuchMethodException
  • NoSuchFieldException
  • IllegalAccessException
  • ClassNotFoundException
  • InstantiationException

6

  • CloneNotSupportedException
  • UnmodifiableClassException
  • InterruptedException
  • LambdaConversionException
  • IOException
  • InterruptedIOException

7

  • UncheckedIOException
  • FileNotFoundException
  • EOFException
  • SyncFailedException
  • ObjectStreamException
  • WriteAbortedException
  • InvalidObjectException
  • StreamCorruptedException
  • InvalidClassException
  • OptionalDataException
  • NotActiveException
  • NotSerializableException
  • UTFDataFormatException

8

  • DateTimeException

  • UnsupportedTemporalTypeException

  • ZoneRulesException

  • DateTimeParseException

  • FormatterClosedException

  • CancellationException

  • UnknownFormatConversionException

  • UnknownFormatFlagsException

  • IllegalFormatFlagsException

  • IllegalFormatPrecisionException

  • IllegalFormatCodePointException

  • MissingFormatArgumentException

  • MissingFormatWidthException

9

  • IllegalFormatWidthException
  • IllegalFormatConversionException
  • DuplicateFormatFlagsException
  • MissingResourceException
  • ConcurrentModificationException
  • RejectedExecutionException
  • CompletionException
  • EmptyStackException
  • IllformedLocaleException
  • NoSuchElementException
  • InputMismatchException
  • ExecutionException
  • InvalidPreferencesFormatException
  • TimeoutException
  • BackingStoreException
  • DataFormatException
  • BrokenBarrierException
  • TooManyListenersException
  • InvalidPropertiesFormatException
  • ZipException
  • JarException

关于对 Java Collection 的支持

与 Java 对接业务中(尤其指对接已有 Java 端)可能遇到 Java 端出于某些原因需要接收非 List 类型集合的情况,如 Set 等。
可以考虑加入对 Java Collection 的序列化与反序列化支持。

Java 端 hessian 序列化非 List 类型集合是序列化成 fixed-length type list 的形式,集合的类型被写在 type 中的一部分。见 com.caucho.hessian.io.CollectionSerializer .

模仿 Java 序列化的方式目前可以通过 go-hessian 的 Serializer 扩展加一个 JavaCollection 接口的形式提供序列化集合的功能(代码见下方),但接收 Java 端集合的情况下,所有集合(被序列化为 fixed-length type list)都会因为头部是 list ('v' 或者 0x70 - 0x77)被 declist 方法反序列化为切片而无法经过 Serializer 提供的反序列化方法(decode.go#line 199 ),可能无法满足两端对象一一对应的业务需求。(即:可以满足发送特定Collection的需求,但无法还原)

可能的解决方案:

对 decode 进行一定的修改,使反序列化对象为 list 的时候,type 也作为选择 dec 方法的依据,
从而让特定 Collection 的反序列化经过 Java Collection 的反序列化器

↓ 一种基于 Serializer 扩展的序列化方案(不包含反序列化)

type JavaCollectionObject interface {
	Get() []interface{}
	JavaClassName() string
}

func init() {
	hessian.SetSerializer("java.util.HashSet", JavaCollectionSerializer{})
}

type JavaCollectionSerializer struct {
}

func (JavaCollectionSerializer) EncObject(e *hessian.Encoder, vv hessian.POJO) error {
	var (
		err error
	)
	v, ok := vv.(JavaCollectionObject)
	if !ok {
		return perrors.New("can not be converted into java collection object")
	}
	collectionName := v.JavaClassName()
	if collectionName == "" {
		return perrors.New("collection name empty")
	}
	list := v.Get()
	length := len(list)
	typeName := v.JavaClassName()
	err = writeCollectionBegin(length, typeName, e)
	if err != nil {
		return err
	}
	for i := 0; i < length; i++ {
		if err = e.Encode(list[i]); err != nil {
			return err
		}
	}
	return nil

}

func (JavaCollectionSerializer) DecObject(d *hessian.Decoder) (interface{}, error) {
	//For collections are coded as lists instead of objects, so the dec method will never be invoked
	return nil, nil
}

func writeCollectionBegin(length int, typeName string, e *hessian.Encoder) error {
	var err error
	if length <= int(hessian.LIST_DIRECT_MAX) {
		e.Append([]byte{hessian.BC_LIST_DIRECT + byte(length)})
		err = e.Encode(typeName)
		if err != nil {
			return err
		}
	} else {
		e.Append([]byte{hessian.BC_LIST_FIXED})
		err = e.Encode(typeName)
		if err != nil {
			return err
		}
		err = e.Encode(int32(length))
		if err != nil {
			return nil
		}
	}
	return nil
} 

some issues needed to fix for v1.5.0

What would you like to be added:

  1. appendix in LICENSE

  2. update the dependency libs with them latest version in go.mod:

  • github.com/dubbogo/gost v1.8.0
  • github.com/pkg/errors v0.9.1
  • github.com/stretchr/testify v1.5.1
  1. add comment for public struct/functions in
  • java_collection.go
  • request.go
  • serialize.go

about Generic$invoke

抱歉英文不好,就用中文了。以上是对泛化调用的需求记录;

目标:实现goclient to javaserver 的泛化调用

以下是实现的探讨,我入门java不到一个月,肯定会有不对的地方,欢迎指出,也希望有java老鸟帮忙看看,十分感谢。

所谓泛化调用是指客户端拿不到服务端的具体类的情况(在go的客户端表现为没有写死的providerserver接口和pojo定义),java的dubbo框架已经有了这个功能,这个功能对于dubbo接口网关类服务的实现是必不可少的。

看了源码我自我以为的java的实现:
java provider端有这样一个interface
image
所有的客户端都调用该接口的$invoke进行访问,将原来的方法、参数、类型都作为请求参数
(我自己测试过,确实能够调通,但由于hessian2的这个问题,没有进入下一步的测试)
如图
image

客户端在没有注册pojo是如何解析返回的数据的。看了源码,invoke方法会将pojo转换成map返回,map中的所有元素都是基本类型,java客户端直接解析map即可,所以目前来看,如果hessain2能够支持不定深度map、数组的解析,应该就可以支持泛化调用。

Bug: 各种包装类型零值问题

What happened:
主要问题:Integer类型null转golang中的int类型报错

可能会出现的其他问题:
基础包装类型0值都可能出现无法接收的情况
基本类型 包装类型
Byte
Integer null值
Short
Long
Float
Double
Boolean
Character
String null值

How to reproduce it (as minimally and precisely as possible):
在golang中专门设置一套可以接收0值得基础类型

Anything else we need to know?:
对于java中的复杂类型,golang接收或多或少会出现一些问题。要么不能接收,要么部分接收(如Date类型null值不能被golang中的time.Time接收)

[Improvement] returned a reflect.value when decoding

https://github.com/dubbogo/hessian2/blob/57ac2e777dc6d853bdcf74b5c697b6a98a728be9/object.go#L305
https://github.com/dubbogo/hessian2/blob/57ac2e777dc6d853bdcf74b5c697b6a98a728be9/object.go#L440
that lines should return vRef.Interface() of value itself instead vRef of reflect.value.

Its effect user decoding what he want, eg:
I registered a POJO named MyUser, when I decode a hessian should return a MyUser type struct to me, but I must doing type assertion twice now for getting type of MyUser:

	RegisterPOJO(new(MyUser))

	d := hessian.NewDecoder(b)
	res, err := d.Decode()
	if err != nil {
		panic(err)
	}

	t.Logf("decode => %+v %v", res.(reflect.Value).Interface(), err)

	user := res.(reflect.Value).Interface().(*MyUser)

Its should using like this for normal user:

	RegisterPOJO(new(MyUser))

	d := hessian.NewDecoder(b)
	res, err := d.Decode()
	if err != nil {
		panic(err)
	}

	t.Logf("decode => %+v %v", res, err)

	user := res.(*MyUser)

反序列化咨询

看起来要求golang的struct必须实现JavaClassName方法。 什么时候在golang中注册javaClassName到struct结构体之间的关联关系?

当golang中反序列化时,在哪里去查javaClassName对应的struct结构体信息

bool pointer

What would you like to be added:
support bool pointer

Why is this needed:
user maybe use pointer to bool like *bool as a struct field, but now, it won't work well.

add feature: 当byte反序列化成object时,使用tag查找属性

如果定义的struct的属性不是以大写的英文开头,比如:

type TestObjectStruct struct {
	_value int
}

func (*TestObjectStruct) JavaClassName() string {
	return "com.caucho.hessian.test.TestObject"
}

那么在反序列化的时候,可能会遇到值绑定不上属性的情形:

https://github.com/dubbogo/hessian2/blob/develop/object.go#L299-L301

所以是不是可以使用tag查找属性,然后把值绑定到属性上:

type TestObjectStruct struct {
	Value int `hessian:"_value"`
}

func (*TestObjectStruct) JavaClassName() string {
	return "com.caucho.hessian.test.TestObject"
}

flat anonymons fields

What would you like to be added:

  • flat anonymons fields
  • ignore field with tag hessian:"-"

Why is this needed:

序列化数组时报no this type name

What happened:序列化数组时报no this type name

//code
package t_test

import (
	hessian "github.com/dubbogo/hessian2"
	"testing"
)

type aaa struct {
	aaa  string
}

func TestHessian(t *testing.T) {

	info := []aaa{{aaa:"vvv"}}

	encoder := hessian.NewEncoder()
	err := encoder.Encode(info)
	if err!=nil{
		t.Error(err)
	}

}
// result
=== RUN   TestHessian
--- FAIL: TestHessian (0.00s)
    hessian_test.go:19: no this type name: t_test.aaa
FAIL

Process finished with exit code 1
// go mod require
github.com/dubbogo/hessian2 v1.2.4

Use go reflection in JIT way

What would you like to be added:

Add cache for go reflection in this package.

Why is this needed:

To make this lib more efficiency.

decode failed when invoke to java server ,and return specially

What happened:
image

decode failed when invoke to java server ,and return specially
success for only JSONObject,success for only JSONArray
but fail with both JSONObject and JSONArray

What you expected to happen:
fix this bug
How to reproduce it (as minimally and precisely as possible):

this is the demo to Reproduction problem:
hessainTest in
https://github.com/pantianying/dubbo-go/blob/hessain_decode_fail/examples/general/dubbo/go-client/app/client.go

getTest in
https://github.com/pantianying/dubbo-go/blob/hessain_decode_fail/examples/general/dubbo/java-server/src/main/java/com/ikurento/user/UserProviderImpl.java

Anything else we need to know?:
this is hessain log:
image

Rft: DubboHeader的一些问题

https://github.com/dubbogo/hessian2/blob/c7d89af14ba3916b66ae09c99f0066d3a3016018/hessian.go#L36-L46

What would you like to be added:

  • 类型问题
    • DubboHeader中的byte, int, PackageType都是平台相关的类型,希望能改成uint8, uint32, uint64
    • DubboHeader中的int64是有符号的,希望能改成uint64
  • Dubbo的报文头如下
    DubboHeader
    希望能使用类似如下类似的表示(格式和顺序与官方的报文头一致):
type DubboHeader struct {
	MagicNumber uint16
	Flat        uint8  // contain Type and SerialID  
	Status      uint8
	InvokeId    uint64
	BodyLength  uint32
}

Why is this needed:

  • 保持类型的平台无关,代码可以更加鲁棒
  • 如果保证DubboHeader与官方的报文头在格式和顺序上的一致,那么可使用如下方式读写DubboHeader:
package main

import {
	"binary"
	"fmt"
}

func main() {
	reader := ... // initialize io.Reader
	writer := ... // initialize io.Writer

	header := DubboHeader{}

	// read
	binary.Read(reader, binary.BigEndian, &header)
	fmt.Println(header.MagicNumber, header.Flat, header.Status, header.InvokeIdheader.BodyLength)

	// write
	binary.Write(writer, binary.BigEndian, &header)
}
  • 上述改动的问题是:改动比较大,很有可能要改动上游的代码,比如dubbo-go

关于map的解析

What happened:

hessian的map解析有两种情况,typeduntyped,问题出在typed的情况。如图:红框所在发生illegal class index @idx %d错误
image

What you expected to happen:

调试之后发现,在第一次得到某个type的时候(如图1:),如果不存在该类型会使用untyped方式继续解析map内容(如图2)。但是如果是map有嵌套或者数组情况,会导致能解析第一个,但是第二个开始就找不到index对应的类型了。如果支持typed map,我们是否应该限制必须注册类型,而不是默认处理为untyped。
image
图1
image
图2

How to reproduce it (as minimally and precisely as possible):

重现可以添加如下的java测试代码:
image

Anything else we need to know?:
另外,下图应该是一个bug,TAG_READ应该换为变量tag
image

Imp: support attachments

In dubbo protocol, attachments need to be supported. And calling method of hessian will be changed in this improvement.

  • Maybe, it could be applyed to define a Request and Response.

can't decode null java date

What happened:
link to: apache/dubbo-go#105

What you expected to happen:
enable to decode null java date object

How to reproduce it (as minimally and precisely as possible):

Anything else we need to know?:

decode时,忽略go结构不存在字段。

What would you like to be added:
目前只能decode和hessian.RegisterPOJO注入的结构完全一致的字段,如果java bean多字段就会报错。
希望可以忽略无法匹配的字段,与json序列化使用方式保持一致。
Why is this needed*:
java服务升级时经常会改动(特别是增加)bean的属性字段,目前的状况是go必须同样升级,这样显然是难以接受的。

Java's Extends and Go's Embed: Java 继承与 Go 嵌入字段

本文描述 dubbo-go-hessian2 如何处理 Java 中的继承。

为便于描述,下文会用一些特定的名词:

  • hessian 表示 Java 版本的 hessian 库
  • go-hessian2 用来指当前库。

Java 继承

Hessian 在序列化继承时,会将父类的属性展开到子类里(效果等同于把这些字段定义在子类里)。也就是说,在 hessian 的编码里,我们无法区分出这个字段是来自当前的类还是父类。所以我们做的工作就是把这些字段一一映射到 Go 的 struct 里。

Go 嵌入字段(匿名字段)

在 Go 里是没有继承这一概念的,但一般的,我们用匿名字段的方式来达到类似继承的字段复用效果。这是我们必须面临的问题,因此我们特别的支持了如下这种情况

Java Go
image image

由于上面提到的原因,我们其实无法识别这个 name 字段是父类的还是子类的,所以对于这个 Java 类,下面这段 Go 定义也是可以识别的。

type Dog struct {
	Name string
}

一般情况下,这些足够使用了。因此对于一些复杂的情况,我们并未完全处理。

暂不支持的情况

Go 里多个匿名字段拥有同样的字段名

type A struct { Name string }
type B struct { Name string }
type C struct {
	A
	B
}

这个结构体 C 有两个叫 Name 的字段,在 Go 官方的 json 库里会把两个都忽略掉。但 go-hessian2 并没有做类似的工作。因为无论怎么处理,都会让人难以理解,开发者应该避免使用这样的用法。如果因为一些失误确实定义了重复的字段,可以使用 hessian:"-" 来忽略其中一个( json 库也支持这样处理)

指针类型的匿名字段

type Dog struct {
	*Animal
}

尽管 Go 里可以这样定义结构体。但是Java 里的父类是不可能为空的,hessian 协议当然也不支持。所以 go-hessian2 也不支持这样的继承。

request.go中的PackRequest方法

args, ok := params.([]interface{}) if !ok { return nil, jerrors.Errorf("@params is not of type: []interface{}") }
这样判断要求传入的params必须这样赋值:body:=[]interface{}{}
如果 body:=[]string{}这样赋值会无法通过这段代码的检查

Go 1.11

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.