Merge pull request #102 from aliiohs/feature/FixDateNilBug

Fix: date nil bug
diff --git a/README.md b/README.md
index 67157ac..019882c 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
 
 ---
 
-It's a golang hessian library used by [Apache/dubbo-go](github.com/apache/dubbo-go).
+It's a golang hessian library used by [Apache/dubbo-go](https://github.com/apache/dubbo-go).
 
 ## Feature List
 
diff --git a/go.sum b/go.sum
index dd2e8c8..e009cc5 100644
--- a/go.sum
+++ b/go.sum
@@ -2,8 +2,6 @@
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dubbogo/gost v1.0.1-0.20190709080436-ae4ac3e96ad1 h1:RrdI0SvwHR2DKNLJj+heaIbeb9qYig9QlniddKB6ViU=
-github.com/dubbogo/gost v1.0.1-0.20190709080436-ae4ac3e96ad1/go.mod h1:R7wZm1DrmrKGr50mBZVcg6C9ekG8aL5hP+sgWcIDwQg=
 github.com/dubbogo/gost v1.1.1 h1:JCM7vx5edPIjDA5ovJTuzEEXuw2t7xLyrlgi2mi5jHI=
 github.com/dubbogo/gost v1.1.1/go.mod h1:R7wZm1DrmrKGr50mBZVcg6C9ekG8aL5hP+sgWcIDwQg=
 github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
diff --git a/hessian.go b/hessian.go
index 1811c55..8fb34ae 100644
--- a/hessian.go
+++ b/hessian.go
@@ -17,7 +17,6 @@
 import (
 	"bufio"
 	"encoding/binary"
-	"reflect"
 	"time"
 )
 
@@ -77,12 +76,13 @@
 		if header.ResponseStatus == Zero {
 			return packRequest(service, header, body)
 		}
-		return packResponse(header, map[string]string{}, body)
+		return packResponse(header, body)
+
 	case PackageRequest, PackageRequest_TwoWay:
 		return packRequest(service, header, body)
 
 	case PackageResponse:
-		return packResponse(header, map[string]string{}, body)
+		return packResponse(header, body)
 
 	default:
 		return nil, perrors.Errorf("Unrecognised message type: %v", header.Type)
@@ -175,16 +175,15 @@
 
 	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, ok := rspObj.(*Response)
+		if !ok {
+			return perrors.Errorf("java exception:%s", exception.(string))
+		}
 		rsp.Exception = perrors.Errorf("java exception:%s", exception.(string))
 		return nil
 	case PackageRequest | PackageHeartbeat, PackageResponse | PackageHeartbeat:
@@ -196,11 +195,7 @@
 		}
 	case PackageResponse:
 		if rspObj != nil {
-			rsp, ok := rspObj.(*Response)
-			if !ok {
-				rsp = &Response{RspObj: rspObj}
-			}
-			if err = unpackResponseBody(buf, rsp); err != nil {
+			if err = unpackResponseBody(buf, rspObj); err != nil {
 				return perrors.WithStack(err)
 			}
 		}
diff --git a/java_exception.go b/java_exception.go
index 010a6dc..abe725d 100644
--- a/java_exception.go
+++ b/java_exception.go
@@ -17,6 +17,7 @@
 import "github.com/dubbogo/hessian2/java_exception"
 
 func init() {
+	RegisterPOJO(&java_exception.Method{})
 	RegisterPOJO(&java_exception.Class{})
 	RegisterPOJO(&java_exception.Throwable{})
 	RegisterPOJO(&java_exception.Exception{})
@@ -104,4 +105,7 @@
 	RegisterPOJO(&java_exception.IllegalFormatCodePointException{})
 	RegisterPOJO(&java_exception.MissingFormatArgumentException{})
 	RegisterPOJO(&java_exception.MissingFormatWidthException{})
+	RegisterPOJO(&java_exception.DubboGenericException{})
+	RegisterPOJO(&java_exception.IncompleteAnnotationException{})
+	RegisterPOJO(&java_exception.AnnotationTypeMismatchException{})
 }
diff --git a/java_exception/annotation_type_mismatch_exception.go b/java_exception/annotation_type_mismatch_exception.go
new file mode 100644
index 0000000..e1b44ff
--- /dev/null
+++ b/java_exception/annotation_type_mismatch_exception.go
@@ -0,0 +1,37 @@
+// Copyright 2016-2019 yiduwangkai@gmail.com
+//
+// 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 java_exception
+
+type AnnotationTypeMismatchException struct {
+	SerialVersionUID     int64
+	DetailMessage        string
+	StackTrace           []StackTraceElement
+	FoundType            string
+	Element              Method
+	SuppressedExceptions []Throwabler
+	Cause                Throwabler
+}
+
+func NewAnnotationTypeMismatchException(detailMessage string) *AnnotationTypeMismatchException {
+	return &AnnotationTypeMismatchException{DetailMessage: detailMessage, StackTrace: []StackTraceElement{}}
+}
+
+func (e AnnotationTypeMismatchException) Error() string {
+	return e.DetailMessage
+}
+
+func (AnnotationTypeMismatchException) JavaClassName() string {
+	return "java.lang.annotation.AnnotationTypeMismatchException"
+}
diff --git a/java_exception/dubbo_generic_exception.go b/java_exception/dubbo_generic_exception.go
new file mode 100644
index 0000000..83cfc8b
--- /dev/null
+++ b/java_exception/dubbo_generic_exception.go
@@ -0,0 +1,37 @@
+// Copyright 2016-2019 tianying Pan
+//
+// 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 java_exception
+
+type DubboGenericException struct {
+	SerialVersionUID     int64
+	DetailMessage        string
+	SuppressedExceptions []Throwabler
+	StackTrace           []StackTraceElement
+	Cause                Throwabler
+	ExceptionClass       string
+	ExceptionMessage     string
+}
+
+func NewDubboGenericException(exceptionClass, exceptionMessage string) *DubboGenericException {
+	return &DubboGenericException{ExceptionClass: exceptionClass, ExceptionMessage: exceptionMessage}
+}
+
+func (e DubboGenericException) Error() string {
+	return e.DetailMessage
+}
+
+func (DubboGenericException) JavaClassName() string {
+	return "com.alibaba.dubbo.rpc.service.GenericException"
+}
diff --git a/java_exception/exception.go b/java_exception/exception.go
index 2a458a2..73a4477 100644
--- a/java_exception/exception.go
+++ b/java_exception/exception.go
@@ -90,6 +90,15 @@
 	Name string
 }
 
+type Method struct {
+	Name string
+}
+
+
+func (Method) JavaClassName() string {
+	return "java.lang.reflect.Method"
+}
+
 func (Class) JavaClassName() string {
 	return "java.lang.Class"
 }
diff --git a/java_exception/incomplete_annotation_exception.go b/java_exception/incomplete_annotation_exception.go
new file mode 100644
index 0000000..b18378a
--- /dev/null
+++ b/java_exception/incomplete_annotation_exception.go
@@ -0,0 +1,37 @@
+// Copyright 2016-2019 yiduwangkai@gmail.com
+//
+// 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 java_exception
+
+type IncompleteAnnotationException struct {
+	SerialVersionUID     int64
+	DetailMessage        string
+	StackTrace           []StackTraceElement
+	ElementName          string
+	AnnotationType       Class
+	SuppressedExceptions []Throwabler
+	Cause                Throwabler
+}
+
+func NewIncompleteAnnotationException(detailMessage string) *IncompleteAnnotationException {
+	return &IncompleteAnnotationException{DetailMessage: detailMessage, StackTrace: []StackTraceElement{}}
+}
+
+func (e IncompleteAnnotationException) Error() string {
+	return e.DetailMessage
+}
+
+func (IncompleteAnnotationException) JavaClassName() string {
+	return "java.lang.annotation.IncompleteAnnotationException"
+}
diff --git a/java_exception_test.go b/java_exception_test.go
index bf5b06e..6af7841 100644
--- a/java_exception_test.go
+++ b/java_exception_test.go
@@ -106,6 +106,9 @@
 	doTestException(t, "throw_IllegalFormatCodePointException", "Code point = 0x1")
 	doTestException(t, "throw_MissingFormatArgumentException", "Format specifier 'MissingFormatArgumentException'")
 	doTestException(t, "throw_MissingFormatWidthException", "MissingFormatWidthException")
+	doTestException(t, "throw_DubboGenericException", "DubboGenericException")
+	doTestException(t, "throw_IncompleteAnnotationException", "java.lang.Override missing element IncompleteAnnotationException")
+	doTestException(t, "throw_AnnotationTypeMismatchException", "Incorrectly typed data found for annotation element null (Found data of type AnnotationTypeMismatchException)")
 }
 
 func doTestException(t *testing.T, method, content string) {
diff --git a/request.go b/request.go
index 58cc051..d7b2d84 100644
--- a/request.go
+++ b/request.go
@@ -146,20 +146,43 @@
 	return types, nil
 }
 
+type Request struct {
+	Params      interface{}
+	Attachments map[string]string
+}
+
+// NewRequest create a new Request
+func NewRequest(params interface{}, atta map[string]string) *Request {
+	if atta == nil {
+		atta = make(map[string]string)
+	}
+	return &Request{
+		Params:      params,
+		Attachments: atta,
+	}
+}
+
+func EnsureRequest(body interface{}) *Request {
+	if req, ok := body.(*Request); ok {
+		return req
+	}
+	return NewRequest(body, nil)
+}
+
 // dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/exchange/codec/ExchangeCodec.java
 // v2.5.4 line 204 encodeRequest
-// todo: attachments
-func packRequest(service Service, header DubboHeader, params interface{}) ([]byte, error) {
+func packRequest(service Service, header DubboHeader, req interface{}) ([]byte, error) {
 	var (
-		err           error
-		types         string
-		byteArray     []byte
-		version       string
-		pkgLen        int
-		serviceParams map[string]string
+		err       error
+		types     string
+		byteArray []byte
+		version   string
+		pkgLen    int
 	)
 
-	args, ok := params.([]interface{})
+	request := EnsureRequest(req)
+
+	args, ok := request.Params.([]interface{})
 	if !ok {
 		return nil, perrors.Errorf("@params is not of type: []interface{}")
 	}
@@ -212,18 +235,17 @@
 		encoder.Encode(v)
 	}
 
-	serviceParams = make(map[string]string)
-	serviceParams[PATH_KEY] = service.Path
-	serviceParams[GROUP_KEY] = service.Group
-	serviceParams[INTERFACE_KEY] = service.Interface
+	request.Attachments[PATH_KEY] = service.Path
+	request.Attachments[GROUP_KEY] = service.Group
+	request.Attachments[INTERFACE_KEY] = service.Interface
 	if len(version) != 0 {
-		serviceParams[VERSION_KEY] = version
+		request.Attachments[VERSION_KEY] = version
 	}
 	if service.Timeout != 0 {
-		serviceParams[TIMEOUT_KEY] = strconv.Itoa(int(service.Timeout / time.Millisecond))
+		request.Attachments[TIMEOUT_KEY] = strconv.Itoa(int(service.Timeout / time.Millisecond))
 	}
 
-	encoder.Encode(serviceParams)
+	encoder.Encode(request.Attachments)
 
 END:
 	byteArray = encoder.Buffer()
diff --git a/response.go b/response.go
index da9bb34..3ca0219 100644
--- a/response.go
+++ b/response.go
@@ -28,19 +28,43 @@
 )
 
 type Response struct {
-	RspObj    interface{}
-	Exception error
-	//Attachments map[string]string
+	RspObj      interface{}
+	Exception   error
+	Attachments map[string]string
+}
+
+// NewResponse create a new Response
+func NewResponse(rspObj interface{}, exception error, attachments map[string]string) *Response {
+	if attachments == nil {
+		attachments = make(map[string]string)
+	}
+	return &Response{
+		RspObj:      rspObj,
+		Exception:   exception,
+		Attachments: attachments,
+	}
+}
+
+func EnsureResponse(body interface{}) *Response {
+	if res, ok := body.(*Response); ok {
+		return res
+	}
+	if exp, ok := body.(error); ok {
+		return NewResponse(nil, exp, nil)
+	}
+	return NewResponse(body, nil, nil)
 }
 
 // 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
-func packResponse(header DubboHeader, attachments map[string]string, ret interface{}) ([]byte, error) {
+func packResponse(header DubboHeader, ret interface{}) ([]byte, error) {
 	var (
 		byteArray []byte
 	)
 
+	response := EnsureResponse(ret)
+
 	hb := header.Type == PackageHeartbeat
 
 	// magic
@@ -70,7 +94,7 @@
 			// com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec.DubboCodec.java
 			// v2.7.1 line191 encodeResponseData
 
-			atta := isSupportResponseAttachment(attachments[DUBBO_VERSION_KEY])
+			atta := isSupportResponseAttachment(response.Attachments[DUBBO_VERSION_KEY])
 
 			var resWithException, resValue, resNullValue int32
 			if atta {
@@ -83,35 +107,33 @@
 				resNullValue = RESPONSE_NULL_VALUE
 			}
 
-			if e, ok := ret.(error); ok { // throw error
+			if response.Exception != nil { // throw error
 				encoder.Encode(resWithException)
-				if t, ok := e.(java_exception.Throwabler); ok {
+				if t, ok := response.Exception.(java_exception.Throwabler); ok {
 					encoder.Encode(t)
 				} else {
-					encoder.Encode(java_exception.NewThrowable(e.Error()))
+					encoder.Encode(java_exception.NewThrowable(response.Exception.Error()))
 				}
 			} else {
-				if ret == nil {
+				if response.RspObj == nil {
 					encoder.Encode(resNullValue)
 				} else {
 					encoder.Encode(resValue)
-					encoder.Encode(ret) // result
+					encoder.Encode(response.RspObj) // result
 				}
 			}
 
 			if atta {
-				encoder.Encode(attachments) // attachments
+				encoder.Encode(response.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)
+		if response.Exception != nil { // throw error
+			encoder.Encode(response.Exception.Error())
 		} else {
-			return nil, perrors.New("Ret must be error or string!")
+			encoder.Encode(response.RspObj)
 		}
 	}
 
@@ -128,8 +150,7 @@
 }
 
 // hessian decode response body
-// todo: need to read attachments
-func unpackResponseBody(buf []byte, response *Response) error {
+func unpackResponseBody(buf []byte, resp interface{}) error {
 	// body
 	decoder := NewDecoder(buf[:])
 	rspType, err := decoder.Decode()
@@ -137,17 +158,32 @@
 		return perrors.WithStack(err)
 	}
 
+	response := EnsureResponse(resp)
+
 	switch rspType {
 	case RESPONSE_WITH_EXCEPTION, RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS:
 		expt, err := decoder.Decode()
 		if err != nil {
 			return perrors.WithStack(err)
 		}
+		if rspType == RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS {
+			attachments, err := decoder.Decode()
+			if err != nil {
+				return perrors.WithStack(err)
+			}
+			atta, ok := attachments.(map[string]string)
+			if ok {
+				response.Attachments = atta
+			} else {
+				return perrors.Errorf("get wrong attachments: %+v", atta)
+			}
+		}
+
 		if e, ok := expt.(error); ok {
 			response.Exception = e
-			return nil
+		} else {
+			response.Exception = perrors.Errorf("got exception: %+v", expt)
 		}
-		response.Exception = perrors.Errorf("got exception: %+v", expt)
 		return nil
 
 	case RESPONSE_VALUE, RESPONSE_VALUE_WITH_ATTACHMENTS:
@@ -155,9 +191,34 @@
 		if err != nil {
 			return perrors.WithStack(err)
 		}
+		if rspType == RESPONSE_VALUE_WITH_ATTACHMENTS {
+			attachments, err := decoder.Decode()
+			if err != nil {
+				return perrors.WithStack(err)
+			}
+			atta, ok := attachments.(map[string]string)
+			if ok {
+				response.Attachments = atta
+			} else {
+				return perrors.Errorf("get wrong attachments: %+v", atta)
+			}
+		}
+
 		return perrors.WithStack(ReflectResponse(rsp, response.RspObj))
 
 	case RESPONSE_NULL_VALUE, RESPONSE_NULL_VALUE_WITH_ATTACHMENTS:
+		if rspType == RESPONSE_NULL_VALUE_WITH_ATTACHMENTS {
+			attachments, err := decoder.Decode()
+			if err != nil {
+				return perrors.WithStack(err)
+			}
+			atta, ok := attachments.(map[string]string)
+			if ok {
+				response.Attachments = atta
+			} else {
+				return perrors.Errorf("get wrong attachments: %+v", atta)
+			}
+		}
 		return nil
 	}
 
diff --git a/test_hessian/pom.xml b/test_hessian/pom.xml
index 031ddd1..e15e228 100644
--- a/test_hessian/pom.xml
+++ b/test_hessian/pom.xml
@@ -25,6 +25,11 @@
             <version>9.4.16.v20190411</version>
             <scope>compile</scope>
         </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>dubbo</artifactId>
+            <version>2.6.5</version>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/test_hessian/src/main/java/test/TestThrowable.java b/test_hessian/src/main/java/test/TestThrowable.java
index faf4b64..7e40b7e 100644
--- a/test_hessian/src/main/java/test/TestThrowable.java
+++ b/test_hessian/src/main/java/test/TestThrowable.java
@@ -14,13 +14,16 @@
 
 package test;
 
+
+import com.alibaba.dubbo.rpc.service.GenericException;
+
 import java.io.*;
-import java.lang.*;
-import java.lang.annotation.*;
+import java.lang.annotation.AnnotationTypeMismatchException;
+import java.lang.annotation.IncompleteAnnotationException;
 import java.lang.instrument.IllegalClassFormatException;
-import java.lang.invoke.WrongMethodTypeException;
-import java.lang.invoke.LambdaConversionException;
 import java.lang.instrument.UnmodifiableClassException;
+import java.lang.invoke.LambdaConversionException;
+import java.lang.invoke.WrongMethodTypeException;
 import java.lang.reflect.*;
 import java.time.DateTimeException;
 import java.time.format.DateTimeParseException;
@@ -390,4 +393,17 @@
   public static Object throw_MissingFormatWidthException() {
     return new MissingFormatWidthException("MissingFormatWidthException");
   }
+
+  public static Object throw_DubboGenericException() {
+    return new GenericException("DubboGenericExceptionClass","DubboGenericException");
+  }
+
+  public static Object throw_IncompleteAnnotationException() {
+    return new IncompleteAnnotationException(Override.class, "IncompleteAnnotationException");
+  }
+
+  public static Object throw_AnnotationTypeMismatchException() {
+    return new AnnotationTypeMismatchException(Override.class.getEnclosingMethod(), "AnnotationTypeMismatchException");
+  }
+
 }