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 }