Merge pull request #57 from fangyincheng/master
Fix:java exception
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/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/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/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/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..3e2fa8b 100644
--- a/test_hessian/pom.xml
+++ b/test_hessian/pom.xml
@@ -1,6 +1,6 @@
<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>
@@ -9,9 +9,15 @@
<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>
diff --git a/test_hessian/src/main/java/test/Hessian.java b/test_hessian/src/main/java/test/Hessian.java
index b5f8e98..4865d3a 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,18 +23,37 @@
public class Hessian {
public static void main(String[] args) throws Exception {
- Method method = null;
- if (args[0].startsWith("throw_")) {
- method = TestThrowable.class.getMethod(args[0]);
- } else {
- method = TestHessian2Servlet.class.getMethod(args[0]);
+ 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();
}
-
- TestHessian2Servlet servlet = new TestHessian2Servlet();
- Object object = method.invoke(servlet);
-
- Hessian2Output output = new Hessian2Output(System.out);
- output.writeObject(object);
- 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