Repository: apache/dubbo-go-hessian2
Pull request #219 (2020-08-15 15:10:37)

java.sql.Time & java.sql.Date
**What this PR does**:
java class java.sql.Time & java.sql.Date refer to hessian.Time & hessian.Date
**Which issue(s) this PR fixes**:

Fixes #

**Special notes for your reviewer**:

**Does this PR introduce a user-facing change?**:
can use hessian.Time & hessian.Date in go for communicating with java.sql.Time & java.sql.Date in java
```release-note

```


Patch:
diff --git a/java_sql_time.go b/java_sql_time.go
new file mode 100644
index 0000000..e6861bb
--- /dev/null
+++ b/java_sql_time.go
@@ -0,0 +1,145 @@
+/*
+ * 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 (
+	"io"
+	"reflect"
+)
+
+import (
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-hessian2/java_sql_time"
+)
+
+func init() {
+	RegisterPOJO(&java_sql_time.Date{})
+	RegisterPOJO(&java_sql_time.Time{})
+	SetJavaSqlTimeSerialize(&java_sql_time.Date{})
+	SetJavaSqlTimeSerialize(&java_sql_time.Time{})
+}
+
+// SetJavaSqlTimeSerialize register serializer for java.sql.Time & java.sql.Date
+func SetJavaSqlTimeSerialize(time java_sql_time.JavaSqlTime) {
+	name := time.JavaClassName()
+	SetSerializer(name, JavaSqlTimeSerializer{})
+}
+
+// JavaSqlTimeSerializer used to encode & decode java.sql.Time & java.sql.Date
+type JavaSqlTimeSerializer struct {
+}
+
+// nolint
+func (JavaSqlTimeSerializer) EncObject(e *Encoder, vv POJO) error {
+
+	var (
+		i         int
+		idx       int
+		err       error
+		clsDef    classInfo
+		className string
+		ptrV      reflect.Value
+	)
+
+	// ensure ptrV is pointer to know vv is type JavaSqlTime or not
+	ptrV = reflect.ValueOf(vv)
+	if reflect.TypeOf(vv).Kind() != reflect.Ptr {
+		ptrV = PackPtr(ptrV)
+	}
+	v, ok := ptrV.Interface().(java_sql_time.JavaSqlTime)
+	if !ok {
+		return perrors.New("can not be converted into java sql time object")
+	}
+	className = v.JavaClassName()
+	if className == "" {
+		return perrors.New("class name empty")
+	}
+	tValue := reflect.ValueOf(vv)
+	// check ref
+	if n, ok := e.checkRefMap(tValue); ok {
+		e.buffer = encRef(e.buffer, n)
+		return nil
+	}
+
+	// write object definition
+	idx = -1
+	for i = range e.classInfoList {
+		if v.JavaClassName() == e.classInfoList[i].javaName {
+			idx = i
+			break
+		}
+	}
+	if idx == -1 {
+		idx, ok = checkPOJORegistry(typeof(vv))
+		if !ok {
+			idx = RegisterPOJO(v)
+		}
+		_, clsDef, err = getStructDefByIndex(idx)
+		if err != nil {
+			return perrors.WithStack(err)
+		}
+		idx = len(e.classInfoList)
+		e.classInfoList = append(e.classInfoList, clsDef)
+		e.buffer = append(e.buffer, clsDef.buffer...)
+	}
+	e.buffer = e.buffer[0 : len(e.buffer)-1]
+	e.buffer = encInt32(e.buffer, 1)
+	e.buffer = encString(e.buffer, "value")
+
+	// write object instance
+	if byte(idx) <= OBJECT_DIRECT_MAX {
+		e.buffer = encByte(e.buffer, byte(idx)+BC_OBJECT_DIRECT)
+	} else {
+		e.buffer = encByte(e.buffer, BC_OBJECT)
+		e.buffer = encInt32(e.buffer, int32(idx))
+	}
+	e.buffer = encDateInMs(e.buffer, v.GetTime())
+	return nil
+}
+
+// nolint
+func (JavaSqlTimeSerializer) DecObject(d *Decoder, typ reflect.Type, cls classInfo) (interface{}, error) {
+
+	if typ.Kind() != reflect.Struct {
+		return nil, perrors.Errorf("wrong type expect Struct but get:%s", typ.String())
+	}
+
+	vRef := reflect.New(typ)
+	// add pointer ref so that ref the same object
+	d.appendRefs(vRef.Interface())
+
+	tag, err := d.readByte()
+	if err == io.EOF {
+		return nil, err
+	}
+	date, err := d.decDate(int32(tag))
+	if err != nil {
+		return nil, perrors.WithStack(err)
+	}
+	sqlTime := vRef.Interface()
+
+	result, ok := sqlTime.(java_sql_time.JavaSqlTime)
+	if !ok {
+		panic("result type is not sql time, please check the whether the conversion is ok")
+	}
+	result.SetTime(date)
+	return result, nil
+}
diff --git a/java_sql_time/date.go b/java_sql_time/date.go
new file mode 100644
index 0000000..3fdc400
--- /dev/null
+++ b/java_sql_time/date.go
@@ -0,0 +1,60 @@
+/*
+ * 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 java_sql_time
+
+import "time"
+
+type Date struct {
+	time.Time
+}
+
+func (d *Date) GetTime() time.Time {
+	return d.Time
+}
+
+func (d *Date) SetTime(time time.Time) {
+	d.Time = time
+}
+
+func (Date) JavaClassName() string {
+	return "java.sql.Date"
+}
+
+func (d *Date) ValueOf(dateStr string) error {
+	time, err := time.Parse("2006-01-02", dateStr)
+	if err != nil {
+		return err
+	}
+	d.Time = time
+	return nil
+}
+
+// nolint
+func (d *Date) Year() int {
+	return d.Time.Year()
+}
+
+// nolint
+func (d *Date) Month() time.Month {
+	return d.Time.Month()
+}
+
+// nolint
+func (d *Date) Day() int {
+	return d.Time.Day()
+}
diff --git a/java_sql_time/java_sql_time.go b/java_sql_time/java_sql_time.go
new file mode 100644
index 0000000..2f73864
--- /dev/null
+++ b/java_sql_time/java_sql_time.go
@@ -0,0 +1,30 @@
+/*
+ * 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 java_sql_time
+
+import "time"
+
+type JavaSqlTime interface {
+	// ValueOf parse time string which format likes '2006-01-02 15:04:05'
+	ValueOf(timeStr string) error
+	// SetTime for decode time
+	SetTime(time time.Time)
+	JavaClassName() string
+	// GetTime used to time
+	GetTime() time.Time
+}
diff --git a/java_sql_time/time.go b/java_sql_time/time.go
new file mode 100644
index 0000000..709cec8
--- /dev/null
+++ b/java_sql_time/time.go
@@ -0,0 +1,60 @@
+/*
+ * 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 java_sql_time
+
+import "time"
+
+type Time struct {
+	time.Time
+}
+
+func (Time) JavaClassName() string {
+	return "java.sql.Time"
+}
+
+func (t *Time) GetTime() time.Time {
+	return t.Time
+}
+
+// nolint
+func (t *Time) Hour() int {
+	return t.Time.Hour()
+}
+
+// nolint
+func (t *Time) Minute() int {
+	return t.Time.Minute()
+}
+
+// nolint
+func (t *Time) Second() int {
+	return t.Time.Second()
+}
+
+func (t *Time) SetTime(time time.Time) {
+	t.Time = time
+}
+
+func (t *Time) ValueOf(timeStr string) error {
+	time, err := time.Parse("15:04:05", timeStr)
+	if err != nil {
+		return err
+	}
+	t.Time = time
+	return nil
+}


Test patch:
diff --git a/java_sql_time_test.go b/java_sql_time_test.go
new file mode 100644
index 0000000..345cfab
--- /dev/null
+++ b/java_sql_time_test.go
@@ -0,0 +1,102 @@
+/*
+ * 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"
+	"time"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go-hessian2/java_sql_time"
+)
+
+func init() {
+	SetJavaSqlTimeSerialize(&java_sql_time.Date{})
+	SetJavaSqlTimeSerialize(&java_sql_time.Time{})
+}
+
+// test local time between go and java
+// go encode
+// java decode
+func TestJavaSqlTimeEncode(t *testing.T) {
+	sqlTime := time.Date(1997, 1, 1, 13, 15, 46, 0, time.UTC)
+	testSqlTime := java_sql_time.Time{Time: sqlTime}
+	testJavaDecode(t, "javaSql_encode_time", testSqlTime)
+
+	sqlDate := time.Date(2020, 8, 9, 0, 0, 0, 0, time.UTC)
+	testSqlDate := java_sql_time.Date{Time: sqlDate}
+	testJavaDecode(t, "javaSql_encode_date", &testSqlDate)
+}
+
+// test local time between go and java
+// java encode
+// go decode
+func TestJavaSqlTimeDecode(t *testing.T) {
+	sqlTime := time.Date(1997, 1, 1, 13, 15, 46, 0, time.UTC)
+	testSqlTime := java_sql_time.Time{Time: sqlTime}
+	testDecodeJavaSqlTime(t, "javaSql_decode_time", &testSqlTime)
+
+	sqlDate := time.Date(2020, 8, 9, 0, 0, 0, 0, time.UTC)
+	testDateTime := java_sql_time.Date{Time: sqlDate}
+	testDecodeJavaSqlTime(t, "javaSql_decode_date", &testDateTime)
+}
+
+func testDecodeJavaSqlTime(t *testing.T, method string, expected java_sql_time.JavaSqlTime) {
+	r, e := decodeJavaResponse(method, "", false)
+	if e != nil {
+		t.Errorf("%s: decode fail with error %v", method, e)
+		return
+	}
+	resultSqlTime, ok := r.(java_sql_time.JavaSqlTime)
+	if !ok {
+		t.Errorf("got error type:%v", r)
+	}
+	assert.Equal(t, resultSqlTime.GetTime().UnixNano(), expected.GetTime().UnixNano())
+}
+
+// test local time between go and go
+// go encode
+// go decode
+func TestJavaSqlTimeWithGo(t *testing.T) {
+	location, _ := time.ParseInLocation("2006-01-02 15:04:05", "1997-01-01 13:15:46", time.Local)
+	sqlTime := java_sql_time.Time{Time: location}
+	e := NewEncoder()
+	e.Encode(&sqlTime)
+	if len(e.Buffer()) == 0 {
+		t.Fail()
+	}
+	d := NewDecoder(e.Buffer())
+	resultSqlTime, _ := d.Decode()
+	assert.Equal(t, &sqlTime, resultSqlTime)
+
+	location, _ = time.ParseInLocation("2006-01-02 15:04:05", "2020-08-09 00:00:00", time.Local)
+	sqlDate := java_sql_time.Date{Time: location}
+	e = NewEncoder()
+	e.Encode(&sqlDate)
+	if len(e.Buffer()) == 0 {
+		t.Fail()
+	}
+	d = NewDecoder(e.Buffer())
+	resultSqlDate, _ := d.Decode()
+	assert.Equal(t, &sqlDate, resultSqlDate)
+}
diff --git a/test_hessian/src/main/java/test/Hessian.java b/test_hessian/src/main/java/test/Hessian.java
index f26cbed..c012885 100644
--- a/test_hessian/src/main/java/test/Hessian.java
+++ b/test_hessian/src/main/java/test/Hessian.java
@@ -62,15 +62,34 @@ public class Hessian {
             Hessian2Output output = new Hessian2Output(System.out);
             output.writeObject(object);
             output.flush();
-        }else if(args[0].startsWith("java8_")){
+        } else if (args[0].startsWith("java8_")) {
             //add java8 java.time Object test
             Method method = TestJava8Time.class.getMethod(args[0]);
             Object obj = new Object();
-            Object object =method.invoke(obj);
+            Object object = method.invoke(obj);
 
             Hessian2Output output = new Hessian2Output(System.out);
             output.writeObject(object);
             output.flush();
+        } else if (args[0].startsWith("javaSql_")) {
+            if (args[0].startsWith("javaSql_encode")) {
+
+                Hessian2Input input = new Hessian2Input(System.in);
+                Object o = input.readObject();
+
+                Method method = TestJavaSqlTime.class.getMethod(args[0], Object.class);
+                TestJavaSqlTime testJavaSqlTime = new TestJavaSqlTime();
+                System.out.print(method.invoke(testJavaSqlTime, o));
+            } else {
+                Method method = TestJavaSqlTime.class.getMethod(args[0]);
+                TestJavaSqlTime testJavaSqlTime = new TestJavaSqlTime();
+                Object object = method.invoke(testJavaSqlTime);
+
+                Hessian2Output output = new Hessian2Output(System.out);
+                output.writeObject(object);
+                output.flush();
+            }
+
         }
     }
 
diff --git a/test_hessian/src/main/java/test/TestJavaSqlTime.java b/test_hessian/src/main/java/test/TestJavaSqlTime.java
new file mode 100644
index 0000000..5316a88
--- /dev/null
+++ b/test_hessian/src/main/java/test/TestJavaSqlTime.java
@@ -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 test;
+
+import java.sql.Date;
+import java.sql.Time;
+
+public class TestJavaSqlTime {
+
+    public Object javaSql_decode_date() {
+        return new Date(1596931200000L);
+    }
+
+    public Object javaSql_decode_time() {
+        return new Time(852124546000L);
+    }
+
+
+    public TestJavaSqlTime() {
+    }
+
+    public static Object javaSql_encode_time(Object v) {
+        return v.equals(new Time(852124546000L));
+    }
+
+    public boolean javaSql_encode_date(Object v) {
+        return v.equals(new Date(1596931200000L));
+    }
+
+}