github.com/jbking/gohan@v0.0.0-20151217002006-b41ccf1c2a96/extension/v8/v8_rpc.go (about)

     1  // Copyright (C) 2015 NTT Innovation Institute, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    12  // implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  // +build v8
    17  // Copyright (C) 2015 NTT Innovation Institute, Inc.
    18  //
    19  // Licensed under the Apache License, Version 2.0 (the "License");
    20  // you may not use this file except in compliance with the License.
    21  // You may obtain a copy of the License at
    22  //
    23  //    http://www.apache.org/licenses/LICENSE-2.0
    24  //
    25  // Unless required by applicable law or agreed to in writing, software
    26  // distributed under the License is distributed on an "AS IS" BASIS,
    27  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    28  // implied.
    29  // See the License for the specific language governing permissions and
    30  // limitations under the License.
    31  
    32  package v8
    33  
    34  import (
    35  	"encoding/json"
    36  	"github.com/ry/v8worker"
    37  	"github.com/twinj/uuid"
    38  	"log"
    39  	"reflect"
    40  )
    41  
    42  //RPCMessage is a struct for rpc message.
    43  type RPCMessage struct {
    44  	Context *Context      `json:"context"`
    45  	Object  string        `json:"object"`
    46  	Method  string        `json:"method"`
    47  	Args    []interface{} `json:"args"`
    48  	Reply   interface{}   `json:"reply"`
    49  }
    50  
    51  //Context has rpc context information
    52  type Context struct {
    53  	ID   string `json:"id"`
    54  	Type string `json:"type"`
    55  }
    56  
    57  //RPC represents go side RPC server
    58  type RPC struct {
    59  	worker    *v8worker.Worker
    60  	callbacks map[string]func(interface{})
    61  	objects   map[string]interface{}
    62  	contexts  map[string]map[string]interface{}
    63  }
    64  
    65  //NewRPC makes RPC Server
    66  func NewRPC() *RPC {
    67  	rpc := &RPC{
    68  		callbacks: map[string]func(interface{}){},
    69  		objects:   map[string]interface{}{},
    70  		contexts:  map[string]map[string]interface{}{},
    71  	}
    72  	rpc.worker = v8worker.New(rpc.Recv)
    73  	return rpc
    74  }
    75  
    76  //Recv recieves message from v8
    77  func (server *RPC) Recv(messageString string) {
    78  	var message RPCMessage
    79  	json.Unmarshal([]byte(messageString), &message)
    80  	server.handleMessage(&message)
    81  }
    82  
    83  //Send sends data for v8
    84  func (server *RPC) Send(message *RPCMessage) error {
    85  	bytes, err := json.Marshal(message)
    86  	if err != nil {
    87  		log.Printf("error %s", err)
    88  		return err
    89  	}
    90  	server.worker.Send(string(bytes))
    91  	return nil
    92  }
    93  
    94  func (server *RPC) handleMessage(message *RPCMessage) {
    95  	contextType := message.Context.Type
    96  	switch contextType {
    97  	case "call":
    98  		server.handleCall(message)
    99  		return
   100  	case "cast":
   101  		server.handleCast(message)
   102  		return
   103  	case "reply":
   104  		server.handleReply(message)
   105  		return
   106  	}
   107  }
   108  
   109  //Invoke calls function by name
   110  func (server *RPC) Invoke(any interface{}, name string, args []interface{}) interface{} {
   111  	inputs := make([]reflect.Value, len(args))
   112  	contextID := args[0].(string)
   113  	context := server.contexts[contextID]
   114  	args[0] = context
   115  	for i, arg := range args {
   116  		inputs[i] = reflect.ValueOf(arg)
   117  	}
   118  	values := reflect.ValueOf(any).MethodByName(name).Call(inputs)
   119  	if len(values) < 1 {
   120  		return nil
   121  	}
   122  	return values[0].Interface()
   123  }
   124  
   125  func (server *RPC) handleCast(message *RPCMessage) {
   126  	log.Printf("cast from js: %s", message.Method)
   127  	object, ok := server.objects[message.Object]
   128  	if !ok {
   129  		return
   130  	}
   131  	server.Invoke(object, message.Method, message.Args)
   132  }
   133  
   134  func (server *RPC) handleCall(message *RPCMessage) {
   135  	log.Printf("call from js: %s", message.Method)
   136  	object, ok := server.objects[message.Object]
   137  	if !ok {
   138  		return
   139  	}
   140  	message.Context.Type = "reply"
   141  	message.Reply = server.Invoke(object, message.Method, message.Args)
   142  	message.Args = nil
   143  	server.Send(message)
   144  }
   145  
   146  func (server *RPC) handleReply(message *RPCMessage) {
   147  	id := message.Context.ID
   148  	callback := server.callbacks[id]
   149  	callback(message.Reply)
   150  	delete(server.callbacks, id)
   151  }
   152  
   153  //Cast send cast message for v8
   154  func (server *RPC) Cast(object, method string, args []interface{}) {
   155  	server.Send(&RPCMessage{
   156  		Context: &Context{
   157  			Type: "cast",
   158  		},
   159  		Object: object,
   160  		Method: method,
   161  		Args:   args,
   162  	})
   163  }
   164  
   165  //Call send call message for v8
   166  func (server *RPC) Call(object, method string, args []interface{}, callback func(interface{})) {
   167  	id := uuid.NewV4().String()
   168  	server.callbacks[id] = callback
   169  	server.Send(&RPCMessage{
   170  		Context: &Context{
   171  			Type: "call",
   172  			ID:   id,
   173  		},
   174  		Method: method,
   175  		Object: object,
   176  		Args:   args,
   177  	})
   178  }
   179  
   180  //Load loads new javascript code
   181  func (server *RPC) Load(source, code string) error {
   182  	return server.worker.Load(source, code)
   183  }
   184  
   185  //BlockCall send call message for v8 and block until response
   186  func (server *RPC) BlockCall(object, method string, args []interface{}) interface{} {
   187  	replyChan := make(chan interface{}, 1)
   188  	server.Call(object, method, args, func(reply interface{}) {
   189  		replyChan <- reply
   190  	})
   191  	return <-replyChan
   192  }
   193  
   194  //RegistObject registers new object
   195  func (server *RPC) RegistObject(objectID string, object interface{}) {
   196  	server.objects[objectID] = object
   197  }