Repository: apache/dubbo-go-hessian2
Pull request #208 (2020-07-01 06:24:02)

Ftr: catch exceptions that user defined
1.当java抛出自定义业务异常的时候，当dubb-go client没有注册这个异常类型，这时候会出现错误。
这里会将这种异常统一转换为BizException。

2.原来的stackTraceElement数据丢失，进行修复。

之前的pr ： https://github.com/apache/dubbo-go-hessian2/pull/204  ，不小心在本地push错远程仓库，直接推进去了。 从代码层面进行了回滚，所以重新提交下PR。



Patch:
diff --git a/java_exception/exception.go b/java_exception/exception.go
index 2b3f5d3..f5a00e9 100644
--- a/java_exception/exception.go
+++ b/java_exception/exception.go
@@ -90,15 +90,15 @@ func (e Exception) GetStackTrace() []StackTraceElement {
 	return e.StackTrace
 }
 
-////////////////////////////
+/////////////////////////////
 // StackTraceElement
-////////////////////////////
+/////////////////////////////
 
 type StackTraceElement struct {
 	DeclaringClass string
 	MethodName     string
 	FileName       string
-	LineNumber     int
+	LineNumber     int32
 }
 
 //JavaClassName  java fully qualified path
diff --git a/java_unknown_exception.go b/java_unknown_exception.go
new file mode 100644
index 0000000..6787e3c
--- /dev/null
+++ b/java_unknown_exception.go
@@ -0,0 +1,88 @@
+/*
+ * 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 hessian
+
+import (
+	"fmt"
+	"sync"
+)
+
+import (
+	"github.com/apache/dubbo-go-hessian2/java_exception"
+)
+
+var mutex sync.Mutex
+
+func checkAndGetException(cls classInfo) (structInfo, bool) {
+
+	if len(cls.fieldNameList) < 4 {
+		return structInfo{}, false
+	}
+	var (
+		throwable structInfo
+		ok        bool
+	)
+	var count = 0
+	for _, item := range cls.fieldNameList {
+		if item == "detailMessage" || item == "suppressedExceptions" || item == "stackTrace" || item == "cause" {
+			count++
+		}
+	}
+	// if have these 4 fields, it is throwable struct
+	if count == 4 {
+		mutex.Lock()
+		defer mutex.Unlock()
+		if throwable, ok = getStructInfo(cls.javaName); ok {
+			return throwable, true
+		}
+		RegisterPOJO(newBizException(cls.javaName))
+		if throwable, ok = getStructInfo(cls.javaName); ok {
+			return throwable, true
+		}
+	}
+	return throwable, count == 4
+}
+
+type UnknownException struct {
+	SerialVersionUID     int64
+	DetailMessage        string
+	SuppressedExceptions []java_exception.Throwabler
+	StackTrace           []java_exception.StackTraceElement
+	Cause                java_exception.Throwabler
+	name                 string
+}
+
+// NewThrowable is the constructor
+func newBizException(name string) *UnknownException {
+	return &UnknownException{name: name, StackTrace: []java_exception.StackTraceElement{}}
+}
+
+// Error output error message
+func (e UnknownException) Error() string {
+	return fmt.Sprintf("throw %v : %v", e.name, e.DetailMessage)
+}
+
+//JavaClassName  java fully qualified path
+func (e UnknownException) JavaClassName() string {
+	return e.name
+}
+
+// equals to getStackTrace in java
+func (e UnknownException) GetStackTrace() []java_exception.StackTraceElement {
+	return e.StackTrace
+}
diff --git a/list.go b/list.go
index 34c8aec..87aef90 100644
--- a/list.go
+++ b/list.go
@@ -29,6 +29,9 @@ import (
 import (
 	perrors "github.com/pkg/errors"
 )
+import (
+	"github.com/apache/dubbo-go-hessian2/java_exception"
+)
 
 var (
 	listTypeNameMapper = &sync.Map{}
@@ -46,6 +49,8 @@ var (
 		"date":             reflect.TypeOf(time.Time{}),
 		"object":           reflect.TypeOf([]Object{}).Elem(),
 		"java.lang.Object": reflect.TypeOf([]Object{}).Elem(),
+		// exception field StackTraceElement
+		"java.lang.StackTraceElement": reflect.TypeOf([]*java_exception.StackTraceElement{}).Elem(),
 	}
 )
 
diff --git a/object.go b/object.go
index e2ef7b3..9148dfa 100644
--- a/object.go
+++ b/object.go
@@ -547,6 +547,10 @@ func (d *Decoder) getStructDefByIndex(idx int) (reflect.Type, classInfo, error)
 	cls = d.classInfoList[idx]
 	s, ok = getStructInfo(cls.javaName)
 	if !ok {
+		// exception
+		if s, ok = checkAndGetException(cls); ok {
+			return s.typ, cls, nil
+		}
 		if !d.isSkip {
 			err = perrors.Errorf("can not find go type name %s in registry", cls.javaName)
 		}


Test patch:
diff --git a/decode_test.go b/decode_test.go
index 3728fec..30468d7 100644
--- a/decode_test.go
+++ b/decode_test.go
@@ -30,6 +30,9 @@ import (
 	"reflect"
 	"testing"
 )
+import (
+	"github.com/apache/dubbo-go-hessian2/java_exception"
+)
 
 const (
 	hessianJar = "test_hessian/target/test_hessian-1.0.0.jar"
@@ -109,7 +112,15 @@ func testDecodeJavaData(t *testing.T, method, className string, skip bool, expec
 	if ok {
 		r = tmp.value.Interface()
 	}
-	if !reflect.DeepEqual(r, expected) {
+	trow, o1 := r.(java_exception.Throwabler)
+	expe, o2 := expected.(java_exception.Throwabler)
+	if o1 && o2 {
+		log.Println(reflect.TypeOf(trow), reflect.TypeOf(trow).Elem().Name())
+		if trow.Error() == expe.Error() && reflect.TypeOf(trow).Elem().Name() == reflect.TypeOf(expe).Elem().Name() {
+			return
+		}
+		t.Errorf("%s: got %v, wanted %v", method, r, expected)
+	} else if !reflect.DeepEqual(r, expected) {
 		t.Errorf("%s: got %v, wanted %v", method, r, expected)
 	}
 }
@@ -127,3 +138,10 @@ func testDecodeFrameworkFunc(t *testing.T, method string, expected func(interfac
 	}
 	expected(r)
 }
+
+func TestUserDefindeException(t *testing.T) {
+	expect := &UnknownException{
+		DetailMessage: "throw UserDefindException",
+	}
+	testDecodeFramework(t, "throw_UserDefindException", expect)
+}
diff --git a/java_unknown_exception_test.go b/java_unknown_exception_test.go
new file mode 100644
index 0000000..ea24321
--- /dev/null
+++ b/java_unknown_exception_test.go
@@ -0,0 +1,45 @@
+/*
+ * 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 hessian
+
+import (
+	"testing"
+)
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+func TestCheckAndGetException(t *testing.T) {
+	clazzInfo1 := classInfo{
+		javaName:      "com.test.UserDefinedException",
+		fieldNameList: []string{"detailMessage", "code", "suppressedExceptions", "stackTrace", "cause"},
+	}
+	s, b := checkAndGetException(clazzInfo1)
+	assert.True(t, b)
+
+	assert.Equal(t, s.javaName, "com.test.UserDefinedException")
+	assert.Equal(t, s.goName, "hessian.UnknownException")
+
+	clazzInfo2 := classInfo{
+		javaName:      "com.test.UserDefinedException",
+		fieldNameList: []string{"detailMessage", "code", "suppressedExceptions", "cause"},
+	}
+	s, b = checkAndGetException(clazzInfo2)
+	assert.False(t, b)
+	assert.Equal(t, s, structInfo{})
+}
diff --git a/test_hessian/src/main/java/test/TestThrowable.java b/test_hessian/src/main/java/test/TestThrowable.java
index 20652ff..3647e67 100644
--- a/test_hessian/src/main/java/test/TestThrowable.java
+++ b/test_hessian/src/main/java/test/TestThrowable.java
@@ -409,4 +409,8 @@ public class TestThrowable {
     return new AnnotationTypeMismatchException(Override.class.getEnclosingMethod(), "AnnotationTypeMismatchException");
   }
 
+  public static Object throw_UserDefindException() {
+      return new UserDefindException("throw UserDefindException");
+  }
+
 }
diff --git a/test_hessian/src/main/java/test/UserDefindException.java b/test_hessian/src/main/java/test/UserDefindException.java
new file mode 100644
index 0000000..26d1db1
--- /dev/null
+++ b/test_hessian/src/main/java/test/UserDefindException.java
@@ -0,0 +1,24 @@
+/*
+ * 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 test;
+
+public class UserDefindException extends RuntimeException{
+    public UserDefindException(String dd){
+        super(dd);
+    }
+}
\ No newline at end of file