github.com/true-sqn/fabric@v2.1.1+incompatible/core/dispatcher/dispatcher.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package dispatcher
     8  
     9  import (
    10  	"reflect"
    11  
    12  	"github.com/golang/protobuf/proto"
    13  	"github.com/pkg/errors"
    14  )
    15  
    16  // Dispatcher is used to handle the boilerplate proto tasks of unmarshaling inputs and remarshaling outputs
    17  // so that the receiver may focus on the implementation details rather than the proto hassles.
    18  type Dispatcher struct {
    19  	// Protobuf should pass through to Google Protobuf in production paths
    20  	Protobuf Protobuf
    21  }
    22  
    23  // Dispatch deserializes the input bytes to the correct type for the method in the receiver, then
    24  // if successful, marshals the output message to bytes and returns it.  On error, it simply returns
    25  // the error.  The method on the receiver must take a single parameter which is a concrete proto
    26  // message type and it should return a proto message and error.
    27  func (d *Dispatcher) Dispatch(inputBytes []byte, methodName string, receiver interface{}) ([]byte, error) {
    28  	method := reflect.ValueOf(receiver).MethodByName(methodName)
    29  
    30  	if method == (reflect.Value{}) {
    31  		return nil, errors.Errorf("receiver %T.%s does not exist", receiver, methodName)
    32  	}
    33  
    34  	if method.Type().NumIn() != 1 {
    35  		return nil, errors.Errorf("receiver %T.%s has %d parameters but expected 1", receiver, methodName, method.Type().NumIn())
    36  	}
    37  
    38  	inputType := method.Type().In(0)
    39  	if inputType.Kind() != reflect.Ptr {
    40  		return nil, errors.Errorf("receiver %T.%s does not accept a pointer as its argument", receiver, methodName)
    41  	}
    42  
    43  	if method.Type().NumOut() != 2 {
    44  		return nil, errors.Errorf("receiver %T.%s returns %d values but expected 2", receiver, methodName, method.Type().NumOut())
    45  	}
    46  
    47  	if !method.Type().Out(0).Implements(reflect.TypeOf((*proto.Message)(nil)).Elem()) {
    48  		return nil, errors.Errorf("receiver %T.%s does not return a an implementor of proto.Message as its first return value", receiver, methodName)
    49  	}
    50  
    51  	if !method.Type().Out(1).Implements(reflect.TypeOf((*error)(nil)).Elem()) {
    52  		return nil, errors.Errorf("receiver %T.%s does not return an error as its second return value", receiver, methodName)
    53  	}
    54  
    55  	inputValue := reflect.New(inputType.Elem())
    56  	inputMsg, ok := inputValue.Interface().(proto.Message)
    57  	if !ok {
    58  		return nil, errors.Errorf("receiver %T.%s does not accept a proto.Message as its argument, it is '%T'", receiver, methodName, inputValue.Interface())
    59  	}
    60  
    61  	err := d.Protobuf.Unmarshal(inputBytes, inputMsg)
    62  	if err != nil {
    63  		return nil, errors.WithMessagef(err, "could not decode input arg for %T.%s", receiver, methodName)
    64  	}
    65  
    66  	outputVals := method.Call([]reflect.Value{inputValue})
    67  
    68  	if !outputVals[1].IsNil() {
    69  		return nil, outputVals[1].Interface().(error)
    70  	}
    71  
    72  	if outputVals[0].IsNil() {
    73  		return nil, errors.Errorf("receiver %T.%s returned (nil, nil) which is not allowed", receiver, methodName)
    74  	}
    75  
    76  	outputMsg := outputVals[0].Interface().(proto.Message)
    77  
    78  	resultBytes, err := d.Protobuf.Marshal(outputMsg)
    79  	if err != nil {
    80  		return nil, errors.WithMessagef(err, "failed to marshal result for %T.%s", receiver, methodName)
    81  	}
    82  
    83  	return resultBytes, nil
    84  }