Merge pull request #61 from cloverstd/master
Fix invalid data
diff --git a/README.md b/README.md
index 074fe4c..165a483 100644
--- a/README.md
+++ b/README.md
@@ -51,7 +51,7 @@
## Customize Usage Examples
-#### Encoding filed name
+#### Encoding filed name
Hessian encoder default converts filed names of struct to lower camelcase, but you can customize it using `hessian` tag.
@@ -76,7 +76,7 @@
if err != nil {
panic(err)
}
-```
+```
The encoded bytes of the struct `MyUser` is as following:
```text
@@ -87,7 +87,7 @@
00000040 0c 30 31 30 2d 31 32 33 34 35 36 37 38 |.010-12345678|
```
-#### Decoding filed name
+#### Decoding filed name
Hessian decoder finds the correct target field though comparing all filed names of struct one by one until matching.
@@ -107,7 +107,7 @@
type MyUser struct {
- MobilePhone string
+ MobilePhone string
}
// The following encoded binary bytes will be hit automatically:
@@ -125,7 +125,7 @@
//
// mobile-phone(tag lookup) => mobilePhone(lowerCameCase) => MobilePhone(SameCase) => mobilephone(lowercase)
// ^ will matched
-//
+//
// 00000000 43 12 63 6f 6d 2e 63 6f 6d 70 61 6e 79 2e 6d 79 |C.com.company.my|
// 00000010 75 73 65 72 91 0b 6d 6f 62 69 6c 65 70 68 6f 6e |user..mobilephon|
// 00000020 65 60 0b 31 37 36 31 32 33 34 31 32 33 34 |e`.17612341234|
@@ -138,7 +138,7 @@
##### hessian.SetTagIdentifier
-You can use `hessian.SetTagIdentifier` to customize tag-identifier of hessian, which takes effect to both encoder and decoder.
+You can use `hessian.SetTagIdentifier` to customize tag-identifier of hessian, which takes effect to both encoder and decoder.
Example:
```go
@@ -163,7 +163,7 @@
if err != nil {
panic(err)
}
-```
+```
The encoded bytes of the struct `MyUser` is as following:
```text
@@ -173,8 +173,3 @@
00000030 4e 75 6d 62 65 72 60 08 75 73 65 72 6e 61 6d 65 |Number`.username|
00000040 0c 30 31 30 2d 31 32 33 34 35 36 37 38 |.010-12345678|
```
-
-
-## Dubbo Service
-
-TODO
diff --git a/const.go b/const.go
index a8ad45b..53a056f 100644
--- a/const.go
+++ b/const.go
@@ -20,6 +20,7 @@
import (
perrors "github.com/pkg/errors"
+ "reflect"
)
const (
@@ -232,3 +233,5 @@
// DescRegex ...
var DescRegex, _ = regexp.Compile(DESC_REGEX)
+
+var NilValue = reflect.Zero(reflect.TypeOf((*interface{})(nil)).Elem())
diff --git a/date_test.go b/date_test.go
index 5e4f789..ed7f96c 100644
--- a/date_test.go
+++ b/date_test.go
@@ -43,7 +43,7 @@
}
func testDateFramework(t *testing.T, method string, expected time.Time) {
- r, e := decodeResponse(method)
+ r, e := decodeJavaResponse(method, "")
if e != nil {
t.Errorf("%s: decode fail with error %+v", method, e)
return
diff --git a/decode_test.go b/decode_test.go
index 0220dc5..8dae2b6 100644
--- a/decode_test.go
+++ b/decode_test.go
@@ -55,9 +55,13 @@
}
}
-func getReply(method string) []byte {
+func getJavaReply(method, className string) []byte {
genHessianJar()
- cmd := exec.Command("java", "-jar", hessianJar, method)
+ cmdArgs := []string{"-jar", hessianJar, method}
+ if className != "" {
+ cmdArgs = append(cmdArgs, className)
+ }
+ cmd := exec.Command("java", cmdArgs...)
out, err := cmd.Output()
if err != nil {
log.Fatal(err)
@@ -65,8 +69,8 @@
return out
}
-func decodeResponse(method string) (interface{}, error) {
- b := getReply(method)
+func decodeJavaResponse(method, className string) (interface{}, error) {
+ b := getJavaReply(method, className)
d := NewDecoder(b)
r, e := d.Decode()
if e != nil {
@@ -76,7 +80,11 @@
}
func testDecodeFramework(t *testing.T, method string, expected interface{}) {
- r, e := decodeResponse(method)
+ testDecodeJavaData(t, method, "", expected)
+}
+
+func testDecodeJavaData(t *testing.T, method, className string, expected interface{}) {
+ r, e := decodeJavaResponse(method, className)
if e != nil {
t.Errorf("%s: decode fail with error %v", method, e)
return
@@ -90,3 +98,17 @@
t.Errorf("%s: got %v, wanted %v", method, r, expected)
}
}
+
+func testDecodeFrameworkFunc(t *testing.T, method string, expected func(interface{})) {
+ r, e := decodeJavaResponse(method, "")
+ if e != nil {
+ t.Errorf("%s: decode fail with error %v", method, e)
+ return
+ }
+
+ tmp, ok := r.(*_refHolder)
+ if ok {
+ r = tmp.value.Interface()
+ }
+ expected(r)
+}
diff --git a/encode.go b/encode.go
index 7a6b082..30d6364 100644
--- a/encode.go
+++ b/encode.go
@@ -70,8 +70,18 @@
case bool:
e.buffer = encBool(e.buffer, val)
- case int8, int16, int32:
- e.buffer = encInt32(e.buffer, v.(int32))
+ case uint8:
+ e.buffer = encInt32(e.buffer, int32(val))
+ case int8:
+ e.buffer = encInt32(e.buffer, int32(val))
+ case int16:
+ e.buffer = encInt32(e.buffer, int32(val))
+ case uint16:
+ e.buffer = encInt32(e.buffer, int32(val))
+ case int32:
+ e.buffer = encInt32(e.buffer, int32(val))
+ case uint32:
+ e.buffer = encInt64(e.buffer, int64(val))
case int:
// if v.(int) >= -2147483648 && v.(int) <= 2147483647 {
@@ -85,6 +95,8 @@
case int64:
e.buffer = encInt64(e.buffer, val)
+ case uint64:
+ e.buffer = encInt64(e.buffer, int64(val))
case time.Time:
e.buffer = encDateInMs(e.buffer, val)
diff --git a/encode_test.go b/encode_test.go
index fd7c49b..462851f 100644
--- a/encode_test.go
+++ b/encode_test.go
@@ -16,6 +16,7 @@
import (
"bytes"
+ "os/exec"
"testing"
)
@@ -24,3 +25,49 @@
t.Fatalf("want %v , got %v", want, got)
}
}
+
+func encodeTarget(target interface{}) ([]byte, error) {
+ e := NewEncoder()
+ err := e.Encode(target)
+ if err != nil {
+ return nil, err
+ }
+ return e.Buffer(), nil
+}
+
+func javaDecodeValidate(method string, target interface{}) (string, error) {
+ b, e := encodeTarget(target)
+ if e != nil {
+ return "", e
+ }
+
+ genHessianJar()
+ cmd := exec.Command("java", "-jar", hessianJar, method)
+
+ stdin, _ := cmd.StdinPipe()
+ _, e = stdin.Write(b)
+ if e != nil {
+ return "", e
+ }
+ e = stdin.Close()
+ if e != nil {
+ return "", e
+ }
+
+ out, e := cmd.Output()
+ if e != nil {
+ return "", e
+ }
+ return string(out), nil
+}
+
+func testJavaDecode(t *testing.T, method string, target interface{}) {
+ r, e := javaDecodeValidate(method, target)
+ if e != nil {
+ t.Errorf("%s: encode fail with error %v", method, e)
+ }
+
+ if r != "true" {
+ t.Errorf("%s: encode %v to bytes wrongly", method, target)
+ }
+}
diff --git a/hessian.go b/hessian.go
index e3e4e66..171191a 100644
--- a/hessian.go
+++ b/hessian.go
@@ -17,6 +17,7 @@
import (
"bufio"
"encoding/binary"
+ "reflect"
"time"
)
@@ -26,11 +27,12 @@
// enum part
const (
- PackageError = PackageType(0x01)
- PackageRequest = PackageType(0x02)
- PackageResponse = PackageType(0x04)
- PackageHeartbeat = PackageType(0x08)
- PackageRequest_TwoWay = PackageType(0x10)
+ PackageError = PackageType(0x01)
+ PackageRequest = PackageType(0x02)
+ PackageResponse = PackageType(0x04)
+ PackageHeartbeat = PackageType(0x08)
+ PackageRequest_TwoWay = PackageType(0x10)
+ PackageResponse_Exception = PackageType(0x20)
)
// PackageType ...
@@ -94,6 +96,9 @@
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)
@@ -128,18 +133,8 @@
} else {
header.Type |= PackageResponse
header.ResponseStatus = buf[3]
-
- // Header{status}
- if buf[3] != Response_OK {
- err = ErrJavaException
- header.Type |= PackageError
- bufSize := h.reader.Buffered()
- if bufSize > 2 { // responseType + objectType + error content,so it's size > 2
- expBuf, expErr := h.reader.Peek(bufSize)
- if expErr == nil {
- err = perrors.Errorf("java exception:%s", string(expBuf[2:bufSize-1]))
- }
- }
+ if header.ResponseStatus != Response_OK {
+ header.Type |= PackageResponse_Exception
}
}
@@ -155,6 +150,10 @@
h.pkgType = header.Type
h.bodyLen = header.BodyLen
+ if h.reader.Buffered() < h.bodyLen {
+ return ErrBodyNotEnough
+ }
+
return perrors.WithStack(err)
}
@@ -162,10 +161,10 @@
// ReadBody uses hessian codec to read response body
func (h *HessianCodec) ReadBody(rspObj interface{}) error {
- buf, err := h.reader.Peek(h.bodyLen)
- if err == bufio.ErrBufferFull {
+ if h.reader.Buffered() < h.bodyLen {
return ErrBodyNotEnough
}
+ buf, err := h.reader.Peek(h.bodyLen)
if err != nil {
return perrors.WithStack(err)
}
@@ -174,21 +173,34 @@
return perrors.WithStack(err)
}
- switch h.pkgType & 0x0f {
- case PackageRequest | PackageHeartbeat, PackageResponse | PackageHeartbeat:
+ switch h.pkgType & 0x2f {
+ case PackageResponse | PackageHeartbeat | PackageResponse_Exception, PackageResponse | PackageResponse_Exception:
+ rsp, ok := rspObj.(*Response)
+ if !ok {
+ return perrors.Errorf("@rspObj is not *Response, it is %s", reflect.TypeOf(rspObj).String())
+ }
+ rsp.Exception = ErrJavaException
+ decoder := NewDecoder(buf[:])
+ exception, err := decoder.Decode()
+ if err != nil {
+ return perrors.WithStack(err)
+ }
+ rsp.Exception = perrors.Errorf("java exception:%s", exception.(string))
return nil
+ case PackageRequest | PackageHeartbeat, PackageResponse | PackageHeartbeat:
case PackageRequest:
if rspObj != nil {
if err = unpackRequestBody(buf, rspObj); err != nil {
return perrors.WithStack(err)
}
}
-
- return nil
-
case PackageResponse:
if rspObj != nil {
- if err = unpackResponseBody(buf, rspObj); err != nil {
+ rsp, ok := rspObj.(*Response)
+ if !ok {
+ rsp = &Response{RspObj: rspObj}
+ }
+ if err = unpackResponseBody(buf, rsp); err != nil {
return perrors.WithStack(err)
}
}
diff --git a/hessian_test.go b/hessian_test.go
index b68c780..45b4768 100644
--- a/hessian_test.go
+++ b/hessian_test.go
@@ -55,67 +55,75 @@
return resp, err
}
-func doTestResponse(t *testing.T, packageType PackageType, responseStatus byte, body interface{}, decodedObject interface{}, assertFunc func()) {
+func doTestResponse(t *testing.T, packageType PackageType, responseStatus byte, body interface{}, decodedResponse *Response, assertFunc func()) {
resp, err := doTestHessianEncodeHeader(t, packageType, responseStatus, body)
codecR := NewHessianCodec(bufio.NewReader(bytes.NewReader(resp)))
h := &DubboHeader{}
err = codecR.ReadHeader(h)
- if responseStatus == Response_OK {
- assert.Nil(t, err)
- } else {
- t.Log(err)
- assert.NotNil(t, err)
- return
- }
+ assert.Nil(t, err)
+
assert.Equal(t, byte(2), h.SerialID)
assert.Equal(t, packageType, h.Type&(PackageRequest|PackageResponse|PackageHeartbeat))
assert.Equal(t, int64(1), h.ID)
assert.Equal(t, responseStatus, h.ResponseStatus)
- err = codecR.ReadBody(decodedObject)
+ err = codecR.ReadBody(decodedResponse)
assert.Nil(t, err)
- t.Log(decodedObject)
+ t.Log(decodedResponse)
if assertFunc != nil {
assertFunc()
return
}
+ if h.ResponseStatus != Zero && h.ResponseStatus != Response_OK {
+ assert.Equal(t, "java exception:"+body.(string), decodedResponse.Exception.Error())
+ return
+ }
+
in, _ := EnsureInterface(UnpackPtrValue(EnsurePackValue(body)), nil)
- out, _ := EnsureInterface(UnpackPtrValue(EnsurePackValue(decodedObject)), nil)
+ out, _ := EnsureInterface(UnpackPtrValue(EnsurePackValue(decodedResponse.RspObj)), nil)
assert.Equal(t, in, out)
}
func TestResponse(t *testing.T) {
caseObj := Case{A: "a", B: 1}
+ decodedResponse := &Response{}
arr := []*Case{&caseObj}
var arrRes []interface{}
- doTestResponse(t, PackageResponse, Response_OK, arr, &arrRes, func() {
+ decodedResponse.RspObj = &arrRes
+ doTestResponse(t, PackageResponse, Response_OK, arr, decodedResponse, func() {
assert.Equal(t, 1, len(arrRes))
assert.Equal(t, &caseObj, arrRes[0])
})
- doTestResponse(t, PackageResponse, Response_OK, &Case{A: "a", B: 1}, &Case{}, nil)
+ decodedResponse.RspObj = &Case{}
+ doTestResponse(t, PackageResponse, Response_OK, &Case{A: "a", B: 1}, decodedResponse, nil)
s := "ok!!!!!"
strObj := ""
- doTestResponse(t, PackageResponse, Response_OK, s, &strObj, nil)
+ decodedResponse.RspObj = &strObj
+ doTestResponse(t, PackageResponse, Response_OK, s, decodedResponse, nil)
var intObj int64
- doTestResponse(t, PackageResponse, Response_OK, int64(3), &intObj, nil)
+ decodedResponse.RspObj = &intObj
+ doTestResponse(t, PackageResponse, Response_OK, int64(3), decodedResponse, nil)
boolObj := false
- doTestResponse(t, PackageResponse, Response_OK, true, &boolObj, nil)
+ decodedResponse.RspObj = &boolObj
+ doTestResponse(t, PackageResponse, Response_OK, true, decodedResponse, nil)
strObj = ""
- doTestResponse(t, PackageResponse, Response_SERVER_ERROR, "error!!!!!", &strObj, nil)
+ decodedResponse.RspObj = &strObj
+ doTestResponse(t, PackageResponse, Response_SERVER_ERROR, "error!!!!!", decodedResponse, nil)
mapObj := map[string][]*Case{"key": {&caseObj}}
mapRes := map[interface{}]interface{}{}
- doTestResponse(t, PackageResponse, Response_OK, mapObj, &mapRes, func() {
+ decodedResponse.RspObj = &mapRes
+ doTestResponse(t, PackageResponse, Response_OK, mapObj, decodedResponse, func() {
c, ok := mapRes["key"]
if !ok {
assert.FailNow(t, "no key in decoded response map")
diff --git a/java_exception.go b/java_exception.go
new file mode 100644
index 0000000..8193062
--- /dev/null
+++ b/java_exception.go
@@ -0,0 +1,89 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed 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 hessian
+
+func init() {
+ RegisterPOJO(&Throwable{})
+ RegisterPOJO(&Exception{})
+ RegisterPOJO(&StackTraceElement{})
+}
+
+////////////////////////////
+// Throwable interface
+////////////////////////////
+
+type Throwabler interface {
+ Error() string
+ JavaClassName() string
+}
+
+////////////////////////////
+// Throwable
+////////////////////////////
+
+type Throwable struct {
+ SerialVersionUID int64
+ DetailMessage string
+ SuppressedExceptions []Throwable
+ StackTrace []StackTraceElement
+ Cause *Throwable
+}
+
+func NewThrowable(detailMessage string) *Throwable {
+ return &Throwable{DetailMessage: detailMessage, StackTrace: []StackTraceElement{}}
+}
+
+func (e Throwable) Error() string {
+ return e.DetailMessage
+}
+
+func (Throwable) JavaClassName() string {
+ return "java.lang.Throwable"
+}
+
+////////////////////////////
+// Exception
+////////////////////////////
+
+type Exception struct {
+ SerialVersionUID int64
+ DetailMessage string
+ SuppressedExceptions []Exception
+ StackTrace []StackTraceElement
+ Cause *Exception
+}
+
+func NewException(detailMessage string) *Exception {
+ return &Exception{DetailMessage: detailMessage, StackTrace: []StackTraceElement{}}
+}
+
+func (e Exception) Error() string {
+ return e.DetailMessage
+}
+
+func (Exception) JavaClassName() string {
+ return "java.lang.Exception"
+}
+
+type StackTraceElement struct {
+ DeclaringClass string
+ MethodName string
+ FileName string
+ LineNumber int
+}
+
+func (StackTraceElement) JavaClassName() string {
+ return "java.lang.StackTraceElement"
+}
diff --git a/java_exception_test.go b/java_exception_test.go
new file mode 100644
index 0000000..2421588
--- /dev/null
+++ b/java_exception_test.go
@@ -0,0 +1,32 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed 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 hessian
+
+import (
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func TestThrowable(t *testing.T) {
+ testDecodeFrameworkFunc(t, "throw_throwable", func(r interface{}) {
+ assert.Equal(t, "exception", r.(error).Error())
+ })
+}
+
+func TestException(t *testing.T) {
+ testDecodeFrameworkFunc(t, "throw_exception", func(r interface{}) {
+ assert.Equal(t, "exception", r.(error).Error())
+ })
+}
diff --git a/list.go b/list.go
index 3525981..c55ef5e 100644
--- a/list.go
+++ b/list.go
@@ -186,16 +186,15 @@
return nil, perrors.WithStack(err)
}
- if it == nil {
- break
- }
-
- v := EnsureRawValue(it)
if isVariableArr {
- aryValue = reflect.Append(aryValue, v)
+ if it != nil {
+ aryValue = reflect.Append(aryValue, EnsureRawValue(it))
+ } else {
+ aryValue = reflect.Append(aryValue, NilValue)
+ }
holder.change(aryValue)
} else {
- SetValue(aryValue.Index(j), v)
+ arr[j] = it
}
}
@@ -239,7 +238,11 @@
}
if isVariableArr {
- aryValue = reflect.Append(aryValue, EnsureRawValue(it))
+ if it != nil {
+ aryValue = reflect.Append(aryValue, EnsureRawValue(it))
+ } else {
+ aryValue = reflect.Append(aryValue, NilValue)
+ }
holder.change(aryValue)
} else {
ary[j] = it
diff --git a/list_test.go b/list_test.go
index 12558c5..1832c8e 100644
--- a/list_test.go
+++ b/list_test.go
@@ -43,6 +43,8 @@
}
func TestList(t *testing.T) {
+ RegisterPOJOs(new(A0), new(A1))
+
testDecodeFramework(t, "replyTypedFixedList_0", []interface{}{})
testDecodeFramework(t, "replyTypedFixedList_1", []interface{}{"1"})
testDecodeFramework(t, "replyTypedFixedList_7", []interface{}{"1", "2", "3", "4", "5", "6", "7"})
@@ -51,4 +53,18 @@
testDecodeFramework(t, "replyUntypedFixedList_1", []interface{}{"1"})
testDecodeFramework(t, "replyUntypedFixedList_7", []interface{}{"1", "2", "3", "4", "5", "6", "7"})
testDecodeFramework(t, "replyUntypedFixedList_8", []interface{}{"1", "2", "3", "4", "5", "6", "7", "8"})
+
+ testDecodeFramework(t, "customReplyTypedFixedListHasNull", []interface{}{new(A0), new(A1), nil})
+ testDecodeFramework(t, "customReplyTypedVariableListHasNull", []interface{}{new(A0), new(A1), nil})
+ testDecodeFramework(t, "customReplyUntypedFixedListHasNull", []interface{}{new(A0), new(A1), nil})
+ testDecodeFramework(t, "customReplyUntypedVariableListHasNull", []interface{}{new(A0), new(A1), nil})
+}
+
+func TestListEncode(t *testing.T) {
+ testJavaDecode(t, "argUntypedFixedList_0", []interface{}{})
+ testJavaDecode(t, "argUntypedFixedList_1", []interface{}{"1"})
+ testJavaDecode(t, "argUntypedFixedList_7", []interface{}{"1", "2", "3", "4", "5", "6", "7"})
+ testJavaDecode(t, "argUntypedFixedList_8", []interface{}{"1", "2", "3", "4", "5", "6", "7", "8"})
+
+ testJavaDecode(t, "customArgUntypedFixedListHasNull", []interface{}{new(A0), new(A1), nil})
}
diff --git a/object.go b/object.go
index 2a13170..b653f6b 100644
--- a/object.go
+++ b/object.go
@@ -330,15 +330,15 @@
fldRawValue := UnpackPtrValue(field)
kind := fldTyp.Kind()
- switch {
- case kind == reflect.String:
+ switch kind {
+ case reflect.String:
str, err := d.decString(TAG_READ)
if err != nil {
return nil, perrors.Wrapf(err, "decInstance->ReadString: %s", fieldName)
}
fldRawValue.SetString(str)
- case kind == reflect.Int32 || kind == reflect.Int16:
+ case reflect.Int32, reflect.Int16, reflect.Int8:
num, err := d.decInt32(TAG_READ)
if err != nil {
// java enum
@@ -351,13 +351,17 @@
enumValue, _ := s.(JavaEnum)
num = int32(enumValue)
} else {
- return nil, perrors.Wrapf(err, "decInstance->ParseInt, field name:%s", fieldName)
+ return nil, perrors.Wrapf(err, "decInstance->decInt32, field name:%s", fieldName)
}
}
-
fldRawValue.SetInt(int64(num))
-
- case kind == reflect.Int || kind == reflect.Int64 || kind == reflect.Uint64:
+ case reflect.Uint16, reflect.Uint8:
+ num, err := d.decInt32(TAG_READ)
+ if err != nil {
+ return nil, perrors.Wrapf(err, "decInstance->decInt32, field name:%s", fieldName)
+ }
+ fldRawValue.SetUint(uint64(num))
+ case reflect.Uint, reflect.Int, reflect.Int64:
num, err := d.decInt64(TAG_READ)
if err != nil {
if fldTyp.Implements(javaEnumType) {
@@ -374,29 +378,34 @@
}
fldRawValue.SetInt(num)
-
- case kind == reflect.Bool:
+ case reflect.Uint32, reflect.Uint64:
+ num, err := d.decInt64(TAG_READ)
+ if err != nil {
+ return nil, perrors.Wrapf(err, "decInstance->decInt64, field name:%s", fieldName)
+ }
+ fldRawValue.SetUint(uint64(num))
+ case reflect.Bool:
b, err := d.Decode()
if err != nil {
return nil, perrors.Wrapf(err, "decInstance->Decode field name:%s", fieldName)
}
fldRawValue.SetBool(b.(bool))
- case kind == reflect.Float32 || kind == reflect.Float64:
+ case reflect.Float32, reflect.Float64:
num, err := d.decDouble(TAG_READ)
if err != nil {
return nil, perrors.Wrapf(err, "decInstance->decDouble field name:%s", fieldName)
}
fldRawValue.SetFloat(num.(float64))
- case kind == reflect.Map:
+ case reflect.Map:
// decode map should use the original field value for correct value setting
err := d.decMapByValue(field)
if err != nil {
return nil, perrors.Wrapf(err, "decInstance->decMapByValue field name: %s", fieldName)
}
- case kind == reflect.Slice || kind == reflect.Array:
+ case reflect.Slice, reflect.Array:
m, err := d.decList(TAG_READ)
if err != nil {
if err == io.EOF {
@@ -410,7 +419,7 @@
if err != nil {
return nil, err
}
- case kind == reflect.Struct:
+ case reflect.Struct:
var (
err error
s interface{}
diff --git a/object_test.go b/object_test.go
index 4a530b2..6c5b44e 100644
--- a/object_test.go
+++ b/object_test.go
@@ -15,6 +15,7 @@
package hessian
import (
+ "math"
"reflect"
"testing"
)
@@ -328,25 +329,27 @@
}
func TestObject(t *testing.T) {
- RegisterPOJO(&A0{})
- RegisterPOJO(&A1{})
- RegisterPOJO(&A2{})
- RegisterPOJO(&A3{})
- RegisterPOJO(&A4{})
- RegisterPOJO(&A5{})
- RegisterPOJO(&A6{})
- RegisterPOJO(&A7{})
- RegisterPOJO(&A8{})
- RegisterPOJO(&A9{})
- RegisterPOJO(&A10{})
- RegisterPOJO(&A11{})
- RegisterPOJO(&A12{})
- RegisterPOJO(&A13{})
- RegisterPOJO(&A14{})
- RegisterPOJO(&A15{})
- RegisterPOJO(&A16{})
- RegisterPOJO(&TestObjectStruct{})
- RegisterPOJO(&TestConsStruct{})
+ RegisterPOJOs(
+ &A0{},
+ &A1{},
+ &A2{},
+ &A3{},
+ &A4{},
+ &A5{},
+ &A6{},
+ &A7{},
+ &A8{},
+ &A9{},
+ &A10{},
+ &A11{},
+ &A12{},
+ &A13{},
+ &A14{},
+ &A15{},
+ &A16{},
+ &TestObjectStruct{},
+ &TestConsStruct{},
+ )
testDecodeFramework(t, "replyObject_0", &A0{})
testDecodeFramework(t, "replyObject_1", &TestObjectStruct{Value: 0})
@@ -363,3 +366,100 @@
cons.Rest = &cons
testDecodeFramework(t, "replyObject_3", &cons)
}
+
+type Tuple struct {
+ Byte int8
+ Short int16
+ Integer int32
+ Long int64
+ Double float32
+ B uint8
+ S uint16
+ I uint32
+ L uint64
+ D float64
+}
+
+func (t Tuple) JavaClassName() string {
+ return "test.tuple.Tuple"
+}
+
+func TestDecodeJavaTupleObject(t *testing.T) {
+ tuple := &Tuple{
+ Byte: 1,
+ Short: 1,
+ Integer: 1,
+ Long: 1,
+ Double: 1.23,
+ B: 0x01,
+ S: 1,
+ I: 1,
+ L: 1,
+ D: 1.23,
+ }
+
+ RegisterPOJO(tuple)
+
+ testDecodeJavaData(t, "getTheTuple", "test.tuple.TupleProviderImpl", tuple)
+}
+
+func TestEncodeDecodeTuple(t *testing.T) {
+ doTestEncodeDecodeTuple(t, &Tuple{
+ Byte: 1,
+ Short: 1,
+ Integer: 1,
+ Long: 1,
+ Double: 1.23,
+ B: 0x01,
+ S: 1,
+ I: 1,
+ L: 1,
+ D: 1.23,
+ })
+
+ doTestEncodeDecodeTuple(t, &Tuple{
+ Byte: math.MinInt8,
+ Short: math.MinInt16,
+ Integer: math.MinInt32,
+ Long: math.MinInt64,
+ Double: -99.99,
+ B: 0x00,
+ S: 0,
+ I: 0,
+ L: 0,
+ D: -9999.9999,
+ })
+
+ doTestEncodeDecodeTuple(t, &Tuple{
+ Byte: math.MaxInt8,
+ Short: math.MaxInt16,
+ Integer: math.MaxInt32,
+ Long: math.MaxInt64,
+ Double: math.MaxFloat32,
+ B: 0xFF,
+ S: 0xFFFF,
+ I: 0xFFFFFFFF,
+ L: 0xFFFFFFFFFFFFFFFF,
+ D: math.MaxFloat64,
+ })
+}
+
+func doTestEncodeDecodeTuple(t *testing.T, tuple *Tuple) {
+ e := NewEncoder()
+ err := e.encObject(tuple)
+ if err != nil {
+ t.Error(err)
+ t.FailNow()
+ }
+
+ d := NewDecoder(e.buffer)
+ decObj, err := d.Decode()
+ if err != nil {
+ t.Error(err)
+ t.FailNow()
+ }
+
+ if !reflect.DeepEqual(tuple, decObj) {
+ t.Errorf("expect: %v, but get: %v", tuple, decObj)
+ }
+}
diff --git a/pojo.go b/pojo.go
index d4403e4..1494b33 100644
--- a/pojo.go
+++ b/pojo.go
@@ -184,6 +184,17 @@
return structInfo.index
}
+// RegisterPOJOs register a POJO instance arr @os. The return value is @os's
+// mathching index array, in which "-1" means its matching POJO has been registered.
+func RegisterPOJOs(os ...POJO) []int {
+ arr := make([]int, len(os))
+ for i := range os {
+ arr[i] = RegisterPOJO(os[i])
+ }
+
+ return arr
+}
+
// RegisterJavaEnum Register a value type JavaEnum variable.
func RegisterJavaEnum(o POJOEnum) int {
var (
diff --git a/ref.go b/ref.go
index 299556a..e9c08d9 100644
--- a/ref.go
+++ b/ref.go
@@ -28,6 +28,10 @@
// record the kind of target, objects are the same only if the address and kind are the same
kind reflect.Kind
+ // Different struct may share the same address and kind,
+ // so using type information to distinguish them.
+ tp reflect.Type
+
// ref index
index int
}
@@ -87,6 +91,7 @@
func (e *Encoder) checkRefMap(v reflect.Value) (int, bool) {
var (
kind reflect.Kind
+ tp reflect.Type
addr unsafe.Pointer
)
@@ -95,6 +100,9 @@
v = v.Elem()
}
kind = v.Elem().Kind()
+ if kind != reflect.Invalid {
+ tp = v.Elem().Type()
+ }
if kind == reflect.Slice || kind == reflect.Map {
addr = unsafe.Pointer(v.Elem().Pointer())
} else {
@@ -102,6 +110,7 @@
}
} else {
kind = v.Kind()
+ tp = v.Type()
switch kind {
case reflect.Slice, reflect.Map:
addr = unsafe.Pointer(v.Pointer())
@@ -111,15 +120,21 @@
}
if elem, ok := e.refMap[addr]; ok {
- // the array addr is equal to the first elem, which must ignore
if elem.kind == kind {
- return elem.index, ok
+ // If kind is not struct, just return the index. Otherwise,
+ // check whether the types are same, because the different
+ // empty struct may share the same address and kind.
+ if elem.kind != reflect.Struct {
+ return elem.index, ok
+ } else if elem.tp == tp {
+ return elem.index, ok
+ }
}
return 0, false
}
n := len(e.refMap)
- e.refMap[addr] = _refElem{kind, n}
+ e.refMap[addr] = _refElem{kind, tp, n}
return 0, false
}
diff --git a/response.go b/response.go
index c610d34..7ccc57e 100644
--- a/response.go
+++ b/response.go
@@ -26,6 +26,12 @@
perrors "github.com/pkg/errors"
)
+type Response struct {
+ RspObj interface{}
+ Exception error
+ //Attachments map[string]string
+}
+
// dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/codec/ExchangeCodec.java
// v2.7.1 line 256 encodeResponse
// hessian encode response
@@ -56,39 +62,55 @@
encoder := NewEncoder()
encoder.Append(byteArray[:HEADER_LENGTH])
- if hb {
- encoder.Encode(nil)
- } else {
- // com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec.DubboCodec.java
- // v2.7.1 line191 encodeRequestData
-
- atta := isSupportResponseAttachment(attachments[DUBBO_VERSION_KEY])
-
- var resWithException, resValue, resNullValue int32
- if atta {
- resWithException = RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS
- resValue = RESPONSE_VALUE_WITH_ATTACHMENTS
- resNullValue = RESPONSE_NULL_VALUE_WITH_ATTACHMENTS
+ if header.ResponseStatus == Response_OK {
+ if hb {
+ encoder.Encode(nil)
} else {
- resWithException = RESPONSE_WITH_EXCEPTION
- resValue = RESPONSE_VALUE
- resNullValue = RESPONSE_NULL_VALUE
- }
+ // com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec.DubboCodec.java
+ // v2.7.1 line191 encodeResponseData
- if e, ok := ret.(error); ok { // throw error
- encoder.Encode(resWithException)
- encoder.Encode(e.Error())
- } else {
- if ret == nil {
- encoder.Encode(resNullValue)
+ atta := isSupportResponseAttachment(attachments[DUBBO_VERSION_KEY])
+
+ 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 {
- encoder.Encode(resValue)
- encoder.Encode(ret) // result
+ resWithException = RESPONSE_WITH_EXCEPTION
+ resValue = RESPONSE_VALUE
+ resNullValue = RESPONSE_NULL_VALUE
+ }
+
+ if e, ok := ret.(error); ok { // throw error
+ encoder.Encode(resWithException)
+ if t, ok := e.(Throwabler); ok {
+ encoder.Encode(t)
+ } else {
+ encoder.Encode(NewThrowable(e.Error()))
+ }
+ } else {
+ if ret == nil {
+ encoder.Encode(resNullValue)
+ } else {
+ encoder.Encode(resValue)
+ encoder.Encode(ret) // result
+ }
+ }
+
+ if atta {
+ encoder.Encode(attachments) // attachments
}
}
-
- if atta {
- encoder.Encode(attachments) // attachments
+ } else {
+ // com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec
+ // v2.6.5 line280 encodeResponse
+ if e, ok := ret.(error); ok { // throw error
+ encoder.Encode(e.Error())
+ } else if e, ok := ret.(string); ok {
+ encoder.Encode(e)
+ } else {
+ return nil, perrors.New("Ret must be error or string!")
}
}
@@ -106,7 +128,7 @@
// hessian decode response body
// todo: need to read attachments
-func unpackResponseBody(buf []byte, rspObj interface{}) error {
+func unpackResponseBody(buf []byte, response *Response) error {
// body
decoder := NewDecoder(buf[:])
rspType, err := decoder.Decode()
@@ -120,17 +142,22 @@
if err != nil {
return perrors.WithStack(err)
}
- return perrors.Errorf("got exception: %+v", expt)
+ if e, ok := expt.(error); ok {
+ response.Exception = e
+ return nil
+ }
+ 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)
}
- return perrors.WithStack(ReflectResponse(rsp, rspObj))
+ return perrors.WithStack(ReflectResponse(rsp, response.RspObj))
case RESPONSE_NULL_VALUE, RESPONSE_NULL_VALUE_WITH_ATTACHMENTS:
- return perrors.New("Received null")
+ return nil
}
return nil
diff --git a/test_dubbo/pom.xml b/test_dubbo/pom.xml
index df315c6..92051cf 100644
--- a/test_dubbo/pom.xml
+++ b/test_dubbo/pom.xml
@@ -14,7 +14,7 @@
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
- <version>2.7.0</version>
+ <version>2.7.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
diff --git a/test_hessian/pom.xml b/test_hessian/pom.xml
index f05ecf9..031ddd1 100644
--- a/test_hessian/pom.xml
+++ b/test_hessian/pom.xml
@@ -1,17 +1,19 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
-
+
<groupId>test</groupId>
<artifactId>test_hessian</artifactId>
<version>1.0.0</version>
- <properties>
- <maven.compiler.source>1.8</maven.compiler.source>
- <maven.compiler.target>1.8</maven.compiler.target>
- </properties>
-
<dependencies>
<dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>hessian-lite</artifactId>
+ <version>3.2.6</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
<groupId>com.caucho</groupId>
<artifactId>hessian</artifactId>
<version>4.0.60</version>
@@ -28,6 +30,13 @@
<build>
<plugins>
<plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.8</source>
+ <target>1.8</target>
+ </configuration>
+ </plugin>
+ <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.1</version>
@@ -39,7 +48,8 @@
</goals>
<configuration>
<transformers>
- <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>test.Hessian</mainClass>
</transformer>
</transformers>
diff --git a/test_hessian/src/main/java/test/Hessian.java b/test_hessian/src/main/java/test/Hessian.java
index 1e23005..5f89645 100644
--- a/test_hessian/src/main/java/test/Hessian.java
+++ b/test_hessian/src/main/java/test/Hessian.java
@@ -14,7 +14,8 @@
package test;
-import com.caucho.hessian.io.Hessian2Output;
+import com.alibaba.com.caucho.hessian.io.Hessian2Input;
+import com.alibaba.com.caucho.hessian.io.Hessian2Output;
import com.caucho.hessian.test.TestHessian2Servlet;
import java.lang.reflect.Method;
@@ -22,13 +23,52 @@
public class Hessian {
public static void main(String[] args) throws Exception {
- Method method = TestHessian2Servlet.class.getMethod(args[0]);
+ if (args.length > 1) {
+ testCustomClassMethod(args[0], args[1]);
+ return;
+ }
- TestHessian2Servlet servlet = new TestHessian2Servlet();
- Object object = method.invoke(servlet);
+ if (args[0].startsWith("reply")) {
+ Method method = TestHessian2Servlet.class.getMethod(args[0]);
+ TestHessian2Servlet servlet = new TestHessian2Servlet();
+ Object object = method.invoke(servlet);
+ Hessian2Output output = new Hessian2Output(System.out);
+ output.writeObject(object);
+ output.flush();
+ } else if (args[0].startsWith("customReply")) {
+ Method method = TestCustomReply.class.getMethod(args[0]);
+ TestCustomReply testCustomReply = new TestCustomReply(System.out);
+ method.invoke(testCustomReply);
+ } else if (args[0].startsWith("arg")) {
+ Hessian2Input input = new Hessian2Input(System.in);
+ Object o = input.readObject();
+
+ Method method = TestHessian2Servlet.class.getMethod(args[0], Object.class);
+ TestHessian2Servlet servlet = new TestHessian2Servlet();
+ System.out.print(method.invoke(servlet, o));
+ } else if (args[0].startsWith("customArg")) {
+ Method method = TestCustomDecode.class.getMethod(args[0]);
+ TestCustomDecode testCustomDecode = new TestCustomDecode(System.in);
+ System.out.print(method.invoke(testCustomDecode));
+ } else if (args[0].startsWith("throw_")) {
+ Method method = method = TestThrowable.class.getMethod(args[0]);
+ TestHessian2Servlet servlet = new TestHessian2Servlet();
+ Object object = method.invoke(servlet);
+
+ Hessian2Output output = new Hessian2Output(System.out);
+ output.writeObject(object);
+ output.flush();
+ }
+ }
+
+ private static void testCustomClassMethod(String methodName, String className) throws Exception {
+ Class<?> clazz = Class.forName(className);
+ Method method = clazz.getMethod(methodName);
+ Object target = clazz.newInstance();
+ Object result = method.invoke(target);
Hessian2Output output = new Hessian2Output(System.out);
- output.writeObject(object);
+ output.writeObject(result);
output.flush();
}
}
\ No newline at end of file
diff --git a/test_hessian/src/main/java/test/TestCustomDecode.java b/test_hessian/src/main/java/test/TestCustomDecode.java
new file mode 100644
index 0000000..05413f0
--- /dev/null
+++ b/test_hessian/src/main/java/test/TestCustomDecode.java
@@ -0,0 +1,43 @@
+// Copyright 2019 Xinge Gao
+//
+// Licensed 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 test;
+
+import com.caucho.hessian.io.Hessian2Input;
+import com.caucho.hessian.test.A0;
+import com.caucho.hessian.test.A1;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class TestCustomDecode {
+
+ private Hessian2Input input;
+
+ TestCustomDecode(InputStream is) {
+ input = new Hessian2Input(is);
+ }
+
+ public Object customArgUntypedFixedListHasNull() throws Exception {
+ List list = new ArrayList();
+ list.add(new A0());
+ list.add(new A1());
+ list.add(null);
+
+ Object o = input.readObject();
+ return list.equals(o);
+ }
+}
\ No newline at end of file
diff --git a/test_hessian/src/main/java/test/TestCustomReply.java b/test_hessian/src/main/java/test/TestCustomReply.java
new file mode 100644
index 0000000..e50a49a
--- /dev/null
+++ b/test_hessian/src/main/java/test/TestCustomReply.java
@@ -0,0 +1,118 @@
+// Copyright 2019 Xinge Gao
+//
+// Licensed 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 test;
+
+import com.alibaba.com.caucho.hessian.io.Hessian2Output;
+import com.caucho.hessian.test.A0;
+import com.caucho.hessian.test.A1;
+
+import java.io.OutputStream;
+import java.util.Date;
+import java.util.HashMap;
+
+
+public class TestCustomReply {
+
+ private Hessian2Output output;
+ private HashMap<Class<?>, String> typeMap;
+
+ TestCustomReply(OutputStream os) {
+ output = new Hessian2Output(os);
+
+ typeMap = new HashMap<>();
+ typeMap.put(Void.TYPE, "void");
+ typeMap.put(Boolean.class, "boolean");
+ typeMap.put(Byte.class, "byte");
+ typeMap.put(Short.class, "short");
+ typeMap.put(Integer.class, "int");
+ typeMap.put(Long.class, "long");
+ typeMap.put(Float.class, "float");
+ typeMap.put(Double.class, "double");
+ typeMap.put(Character.class, "char");
+ typeMap.put(String.class, "string");
+ typeMap.put(StringBuilder.class, "string");
+ typeMap.put(Object.class, "object");
+ typeMap.put(Date.class, "date");
+ typeMap.put(Boolean.TYPE, "boolean");
+ typeMap.put(Byte.TYPE, "byte");
+ typeMap.put(Short.TYPE, "short");
+ typeMap.put(Integer.TYPE, "int");
+ typeMap.put(Long.TYPE, "long");
+ typeMap.put(Float.TYPE, "float");
+ typeMap.put(Double.TYPE, "double");
+ typeMap.put(Character.TYPE, "char");
+ typeMap.put(boolean[].class, "[boolean");
+ typeMap.put(byte[].class, "[byte");
+ typeMap.put(short[].class, "[short");
+ typeMap.put(int[].class, "[int");
+ typeMap.put(long[].class, "[long");
+ typeMap.put(float[].class, "[float");
+ typeMap.put(double[].class, "[double");
+ typeMap.put(char[].class, "[char");
+ typeMap.put(String[].class, "[string");
+ typeMap.put(Object[].class, "[object");
+ }
+
+ public void customReplyTypedFixedListHasNull() throws Exception {
+ Object[] o = new Object[]{new A0(), new A1(), null};
+ output.writeObject(o);
+ output.flush();
+ }
+
+ public void customReplyTypedVariableListHasNull() throws Exception {
+ Object[] o = new Object[]{new A0(), new A1(), null};
+ if (output.addRef(o)) {
+ return;
+ }
+ boolean hasEnd = output.writeListBegin(-1, typeMap.get(o.getClass()));
+ for (Object tmp: o) {
+ output.writeObject(tmp);
+ }
+ if (hasEnd) {
+ output.writeListEnd();
+ }
+ output.flush();
+ }
+
+ public void customReplyUntypedFixedListHasNull() throws Exception {
+ Object[] o = new Object[]{new A0(), new A1(), null};
+ if (output.addRef(o)) {
+ return;
+ }
+ boolean hasEnd = output.writeListBegin(o.length, null);
+ for (Object tmp: o) {
+ output.writeObject(tmp);
+ }
+ if (hasEnd) {
+ output.writeListEnd();
+ }
+ output.flush();
+ }
+
+ public void customReplyUntypedVariableListHasNull() throws Exception {
+ Object[] o = new Object[]{new A0(), new A1(), null};
+ if (output.addRef(o)) {
+ return;
+ }
+ boolean hasEnd = output.writeListBegin(-1, null);
+ for (Object tmp: o) {
+ output.writeObject(tmp);
+ }
+ if (hasEnd) {
+ output.writeListEnd();
+ }
+ output.flush();
+ }
+}
\ No newline at end of file
diff --git a/test_hessian/src/main/java/test/TestThrowable.java b/test_hessian/src/main/java/test/TestThrowable.java
new file mode 100644
index 0000000..9c9134f
--- /dev/null
+++ b/test_hessian/src/main/java/test/TestThrowable.java
@@ -0,0 +1,24 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed 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 test;
+
+public class TestThrowable {
+ public static Object throw_exception() {
+ return new Exception("exception");
+ }
+ public static Object throw_throwable() {
+ return new Throwable("exception");
+ }
+}
diff --git a/test_hessian/src/main/java/test/tuple/Tuple.java b/test_hessian/src/main/java/test/tuple/Tuple.java
new file mode 100644
index 0000000..3854a91
--- /dev/null
+++ b/test_hessian/src/main/java/test/tuple/Tuple.java
@@ -0,0 +1,99 @@
+package test.tuple;
+
+import java.io.Serializable;
+
+public class Tuple implements Serializable {
+ private static final long serialVersionUID = -1L;
+
+ Integer Integer;
+ Byte Byte;
+ Short Short;
+ Long Long;
+ Double Double;
+ int i;
+ byte b;
+ short s;
+ long l;
+ double d;
+
+
+ public java.lang.Integer getInteger() {
+ return Integer;
+ }
+
+ public void setInteger(java.lang.Integer integer) {
+ Integer = integer;
+ }
+
+ public java.lang.Byte getByte() {
+ return Byte;
+ }
+
+ public void setByte(java.lang.Byte aByte) {
+ Byte = aByte;
+ }
+
+ public java.lang.Short getShort() {
+ return Short;
+ }
+
+ public void setShort(java.lang.Short aShort) {
+ Short = aShort;
+ }
+
+ public java.lang.Long getLong() {
+ return Long;
+ }
+
+ public void setLong(java.lang.Long aLong) {
+ Long = aLong;
+ }
+
+ public int getI() {
+ return i;
+ }
+
+ public void setI(int i) {
+ this.i = i;
+ }
+
+ public byte getB() {
+ return b;
+ }
+
+ public void setB(byte b) {
+ this.b = b;
+ }
+
+ public short getS() {
+ return s;
+ }
+
+ public void setS(short s) {
+ this.s = s;
+ }
+
+ public long getL() {
+ return l;
+ }
+
+ public void setL(long l) {
+ this.l = l;
+ }
+
+ public java.lang.Double getDouble() {
+ return Double;
+ }
+
+ public void setDouble(java.lang.Double aDouble) {
+ Double = aDouble;
+ }
+
+ public double getD() {
+ return d;
+ }
+
+ public void setD(double d) {
+ this.d = d;
+ }
+}
\ No newline at end of file
diff --git a/test_hessian/src/main/java/test/tuple/TupleProvider.java b/test_hessian/src/main/java/test/tuple/TupleProvider.java
new file mode 100644
index 0000000..075a098
--- /dev/null
+++ b/test_hessian/src/main/java/test/tuple/TupleProvider.java
@@ -0,0 +1,6 @@
+package test.tuple;
+
+public interface TupleProvider {
+
+ Tuple getTheTuple();
+}
diff --git a/test_hessian/src/main/java/test/tuple/TupleProviderImpl.java b/test_hessian/src/main/java/test/tuple/TupleProviderImpl.java
new file mode 100644
index 0000000..5237680
--- /dev/null
+++ b/test_hessian/src/main/java/test/tuple/TupleProviderImpl.java
@@ -0,0 +1,21 @@
+package test.tuple;
+
+public class TupleProviderImpl implements TupleProvider {
+
+ @Override
+ public Tuple getTheTuple() {
+ Tuple result = new Tuple();
+ result.setB((byte) 1);
+ result.setByte(Byte.valueOf("1"));
+ result.setI(1);
+ result.setInteger(Integer.valueOf("1"));
+ result.setL(1L);
+ result.setLong(Long.valueOf("1"));
+ result.setS((short) 1);
+ result.setShort(Short.valueOf("1"));
+ result.setD(1.23);
+ result.setDouble(Double.valueOf("1.23"));
+ return result;
+ }
+
+}