Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 23 additions & 32 deletions protocol/dubbo/hessian2/hessian_response.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,49 +41,40 @@ type DubboResponse struct {
Attachments map[string]any
}

// GenericException keeps Java exception class and message.
type GenericException struct {
ExceptionClass string
ExceptionMessage string
}

// Error returns a readable error string.
func (e GenericException) Error() string {
if e.ExceptionClass == "" {
return e.ExceptionMessage
}
if e.ExceptionMessage == "" {
return e.ExceptionClass
}
return "java exception: " + e.ExceptionClass + " - " + e.ExceptionMessage
}

// ToGenericException converts decoded exception to GenericException when possible.
func ToGenericException(expt any) (*GenericException, bool) {
// ToGenericException converts decoded exception to DubboGenericException when possible.
func ToGenericException(expt any) (*java_exception.DubboGenericException, bool) {
switch v := expt.(type) {
case *GenericException:
return v, true
case GenericException:
return &v, true
case *java_exception.DubboGenericException:
return &GenericException{ExceptionClass: v.ExceptionClass, ExceptionMessage: v.ExceptionMessage}, true
return v, true
case java_exception.DubboGenericException:
return &GenericException{ExceptionClass: v.ExceptionClass, ExceptionMessage: v.ExceptionMessage}, true
return &v, true
case java_exception.Throwabler:
return &GenericException{ExceptionClass: v.JavaClassName(), ExceptionMessage: v.Error()}, true
return newGenericException(v.JavaClassName(), v.Error()), true
case string:
return parseLegacyException(v), true
}
return nil, false
}

func parseLegacyException(exStr string) *GenericException {
func parseLegacyException(exStr string) *java_exception.DubboGenericException {
const prefix = "java exception:"
msg := strings.TrimSpace(exStr)
if strings.HasPrefix(msg, prefix) {
msg = strings.TrimSpace(strings.TrimPrefix(msg, prefix))
}
return &GenericException{ExceptionClass: "java.lang.Exception", ExceptionMessage: msg}
return newGenericException("java.lang.Exception", msg)
}

func newGenericException(exceptionClass, exceptionMessage string) *java_exception.DubboGenericException {
exception := java_exception.NewDubboGenericException(exceptionClass, exceptionMessage)
if exceptionClass == "" {
exception.DetailMessage = exceptionMessage
} else if exceptionMessage == "" {
exception.DetailMessage = exceptionClass
} else {
exception.DetailMessage = "java exception: " + exceptionClass + " - " + exceptionMessage
}
return exception
}

// NewResponse create a new DubboResponse
Expand Down Expand Up @@ -163,10 +154,10 @@ func packResponse(header DubboHeader, ret any) ([]byte, error) {
return nil, perrors.Errorf("encoding response failed: %v", err)
}
switch ex := response.Exception.(type) {
case *GenericException:
err = encoder.Encode(java_exception.NewDubboGenericException(ex.ExceptionClass, ex.ExceptionMessage))
case GenericException:
err = encoder.Encode(java_exception.NewDubboGenericException(ex.ExceptionClass, ex.ExceptionMessage))
case *java_exception.DubboGenericException:
err = encoder.Encode(ex)
case java_exception.DubboGenericException:
err = encoder.Encode(ex)
case java_exception.Throwabler:
err = encoder.Encode(ex)
default:
Expand Down
177 changes: 177 additions & 0 deletions protocol/dubbo/hessian2/hessian_response_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,16 @@
package hessian2

import (
"bufio"
"bytes"
"reflect"
"sync"
"testing"
)

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

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -205,6 +208,180 @@ func TestIsSupportResponseAttachmentConcurrent(t *testing.T) {
wg.Wait()
}

func TestToGenericExceptionUsesHessianExceptionType(t *testing.T) {
exception, ok := ToGenericException(java_exception.DubboGenericException{
ExceptionClass: "com.example.UserNotFoundException",
ExceptionMessage: "user not found",
})

require.True(t, ok)
require.IsType(t, &java_exception.DubboGenericException{}, exception)
assert.Equal(t, "com.example.UserNotFoundException", exception.ExceptionClass)
assert.Equal(t, "user not found", exception.ExceptionMessage)
}

func TestToGenericExceptionConversions(t *testing.T) {
pointerException := java_exception.NewDubboGenericException("com.example.PointerException", "pointer message")

tests := []struct {
name string
input any
wantOK bool
wantClass string
wantMessage string
wantDetailString string
}{
{
name: "generic exception pointer",
input: pointerException,
wantOK: true,
wantClass: "com.example.PointerException",
wantMessage: "pointer message",
},
{
name: "throwable",
input: java_exception.NewThrowable("throwable message"),
wantOK: true,
wantClass: "java.lang.Throwable",
wantMessage: "throwable message",
wantDetailString: "java exception: java.lang.Throwable - throwable message",
},
{
name: "legacy exception string",
input: "java exception: user not found",
wantOK: true,
wantClass: "java.lang.Exception",
wantMessage: "user not found",
wantDetailString: "java exception: java.lang.Exception - user not found",
},
{
name: "plain string",
input: "plain failure",
wantOK: true,
wantClass: "java.lang.Exception",
wantMessage: "plain failure",
wantDetailString: "java exception: java.lang.Exception - plain failure",
},
{
name: "unsupported type",
input: 42,
wantOK: false,
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
exception, ok := ToGenericException(test.input)
assert.Equal(t, test.wantOK, ok)
if !test.wantOK {
assert.Nil(t, exception)
return
}

require.NotNil(t, exception)
assert.Equal(t, test.wantClass, exception.ExceptionClass)
assert.Equal(t, test.wantMessage, exception.ExceptionMessage)
assert.Equal(t, test.wantDetailString, exception.Error())
})
Comment thread
leno23 marked this conversation as resolved.
}
}

func TestNewGenericExceptionDetailMessage(t *testing.T) {
tests := []struct {
name string
class string
message string
wantDetails string
}{
{
name: "message only",
message: "message only",
wantDetails: "message only",
},
{
name: "class only",
class: "com.example.EmptyMessage",
wantDetails: "com.example.EmptyMessage",
},
{
name: "class and message",
class: "com.example.UserNotFoundException",
message: "user not found",
wantDetails: "java exception: com.example.UserNotFoundException - user not found",
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
exception := newGenericException(test.class, test.message)
assert.Equal(t, test.class, exception.ExceptionClass)
assert.Equal(t, test.message, exception.ExceptionMessage)
assert.Equal(t, test.wantDetails, exception.Error())
})
}
}

func TestPackResponseWithGenericExceptionPointer(t *testing.T) {
tests := []struct {
name string
exception error
wantClass string
wantMessage string
}{
{
name: "generic exception pointer",
exception: java_exception.NewDubboGenericException(
"com.example.UserNotFoundException",
"user not found",
),
wantClass: "com.example.UserNotFoundException",
wantMessage: "user not found",
},
{
name: "generic exception value",
exception: java_exception.DubboGenericException{
ExceptionClass: "com.example.IllegalStateException",
ExceptionMessage: "illegal state",
},
wantClass: "com.example.IllegalStateException",
wantMessage: "illegal state",
},
{
name: "throwable",
exception: java_exception.NewThrowable("throwable message"),
wantClass: "java.lang.Throwable",
wantMessage: "throwable message",
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
body := NewResponse(nil, test.exception, map[string]any{})
data, err := packResponse(DubboHeader{
SerialID: 2,
Type: PackageResponse,
ID: 1,
ResponseStatus: Response_OK,
}, body)
require.NoError(t, err)

codecR := NewHessianCodec(bufio.NewReader(bytes.NewReader(data)))
header := &DubboHeader{}
require.NoError(t, codecR.ReadHeader(header))
assert.Equal(t, PackageResponse, header.Type&PackageResponse)
assert.Equal(t, Response_OK, header.ResponseStatus)

decodedResponse := &DubboResponse{}
require.NoError(t, codecR.ReadBody(decodedResponse))

ge, ok := decodedResponse.Exception.(*java_exception.DubboGenericException)
require.True(t, ok)
assert.Equal(t, test.wantClass, ge.ExceptionClass)
assert.Equal(t, test.wantMessage, ge.ExceptionMessage)
})
}
}

func TestVersion2Int(t *testing.T) {
v := version2Int("2.1.3")
assert.Equal(t, 2010300, v)
Expand Down
8 changes: 4 additions & 4 deletions protocol/dubbo/impl/hessian.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,10 @@ func marshalResponse(encoder *hessian.Encoder, p DubboPackage) ([]byte, error) {
if response.Exception != nil { // throw error
_ = encoder.Encode(resWithException)
switch ex := response.Exception.(type) {
case *hessian2.GenericException:
_ = encoder.Encode(java_exception.NewDubboGenericException(ex.ExceptionClass, ex.ExceptionMessage))
case hessian2.GenericException:
_ = encoder.Encode(java_exception.NewDubboGenericException(ex.ExceptionClass, ex.ExceptionMessage))
case *java_exception.DubboGenericException:
_ = encoder.Encode(ex)
case java_exception.DubboGenericException:
_ = encoder.Encode(ex)
case java_exception.Throwabler:
_ = encoder.Encode(ex)
default:
Expand Down
40 changes: 36 additions & 4 deletions protocol/dubbo/impl/hessian_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import (

import (
"dubbo.apache.org/dubbo-go/v3/common"
"dubbo.apache.org/dubbo-go/v3/protocol/dubbo/hessian2"
)

const (
Expand Down Expand Up @@ -512,7 +511,7 @@ func TestMarshalResponse(t *testing.T) {
ResponseStatus: Response_OK,
},
Body: &ResponsePayload{
Exception: hessian2.GenericException{
Exception: java_exception.DubboGenericException{
ExceptionClass: "com.example.UserNotFoundException",
ExceptionMessage: "user not found",
},
Expand Down Expand Up @@ -543,6 +542,39 @@ func TestMarshalResponse(t *testing.T) {
}
})

t.Run("response with generic exception pointer", func(t *testing.T) {
encoder := hessian.NewEncoder()
pkg := DubboPackage{
Header: DubboHeader{
Type: PackageResponse,
ResponseStatus: Response_OK,
},
Body: &ResponsePayload{
Exception: java_exception.NewDubboGenericException(
"com.example.UserNotFoundException",
"user not found",
),
Attachments: map[string]any{},
},
}

data, err := marshalResponse(encoder, pkg)
require.NoError(t, err)
assert.NotNil(t, data)

decoder := hessian.NewDecoder(data)
rspType, err := decoder.Decode()
require.NoError(t, err)
assert.EqualValues(t, RESPONSE_WITH_EXCEPTION, rspType)

expt, err := decoder.Decode()
require.NoError(t, err)
ge, ok := expt.(*java_exception.DubboGenericException)
require.True(t, ok)
assert.Equal(t, "com.example.UserNotFoundException", ge.ExceptionClass)
assert.Equal(t, "user not found", ge.ExceptionMessage)
})

t.Run("response with throwable exception", func(t *testing.T) {
encoder := hessian.NewEncoder()
pkg := DubboPackage{
Expand Down Expand Up @@ -721,7 +753,7 @@ func TestUnmarshalResponseBody(t *testing.T) {
require.NoError(t, err)

response := EnsureResponsePayload(pkg.Body)
ge, ok := response.Exception.(*hessian2.GenericException)
ge, ok := response.Exception.(*java_exception.DubboGenericException)
require.True(t, ok)
assert.Equal(t, "com.example.UserNotFoundException", ge.ExceptionClass)
assert.Equal(t, "user not found", ge.ExceptionMessage)
Expand Down Expand Up @@ -787,7 +819,7 @@ func TestUnmarshalResponseBody(t *testing.T) {
require.NoError(t, err)

response := EnsureResponsePayload(pkg.Body)
ge, ok := response.Exception.(*hessian2.GenericException)
ge, ok := response.Exception.(*java_exception.DubboGenericException)
require.True(t, ok)
assert.Equal(t, "java.lang.Exception", ge.ExceptionClass)
assert.Equal(t, "user not found", ge.ExceptionMessage)
Expand Down
Loading