blob: eb3cb71e9c8a618536d10e2355347607f805e647 [file] [log] [blame]
khenaidoobf6e7bb2018-08-14 22:27:29 -04001/*
2 * Copyright 2018-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Stephane Barbariedc5022d2018-11-19 15:21:44 -050016
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040017package model
18
19import (
Stephane Barbarie694e2b92018-09-07 12:17:36 -040020 "crypto/md5"
Stephane Barbarie694e2b92018-09-07 12:17:36 -040021 "errors"
khenaidoob9203542018-09-17 22:56:37 -040022 "fmt"
23 "github.com/opencord/voltha-go/common/log"
24 "reflect"
Stephane Barbarie694e2b92018-09-07 12:17:36 -040025 "runtime"
khenaidoob9203542018-09-17 22:56:37 -040026 "strings"
Stephane Barbariea188d942018-10-16 16:43:04 -040027 "sync"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040028)
29
Stephane Barbariedc5022d2018-11-19 15:21:44 -050030// OperationContext holds details on the information used during an operation
Stephane Barbarieec0919b2018-09-05 14:14:29 -040031type OperationContext struct {
32 Path string
33 Data interface{}
34 FieldName string
35 ChildKey string
36}
37
Stephane Barbariedc5022d2018-11-19 15:21:44 -050038// NewOperationContext instantiates a new OperationContext structure
Stephane Barbarieec0919b2018-09-05 14:14:29 -040039func NewOperationContext(path string, data interface{}, fieldName string, childKey string) *OperationContext {
40 oc := &OperationContext{
41 Path: path,
42 Data: data,
43 FieldName: fieldName,
44 ChildKey: childKey,
45 }
46 return oc
47}
48
Stephane Barbariedc5022d2018-11-19 15:21:44 -050049// Update applies new data to the context structure
Stephane Barbarieec0919b2018-09-05 14:14:29 -040050func (oc *OperationContext) Update(data interface{}) *OperationContext {
51 oc.Data = data
52 return oc
53}
54
Stephane Barbariedc5022d2018-11-19 15:21:44 -050055// Proxy holds the information for a specific location with the data model
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040056type Proxy struct {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050057 sync.RWMutex
Stephane Barbariec53a2752019-03-08 17:50:10 -050058 Root *root
59 Node *node
60 ParentNode *node
61 Path string
62 FullPath string
63 Exclusive bool
64 Callbacks map[CallbackType]map[string]*CallbackTuple
65 Operation ProxyOperation
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040066}
67
Stephane Barbariedc5022d2018-11-19 15:21:44 -050068// NewProxy instantiates a new proxy to a specific location
Stephane Barbarie1ab43272018-12-08 21:42:13 -050069func NewProxy(root *root, node *node, parentNode *node, path string, fullPath string, exclusive bool) *Proxy {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050070 callbacks := make(map[CallbackType]map[string]*CallbackTuple)
Stephane Barbariea188d942018-10-16 16:43:04 -040071 if fullPath == "/" {
72 fullPath = ""
73 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040074 p := &Proxy{
Stephane Barbariec53a2752019-03-08 17:50:10 -050075 Root: root,
76 Node: node,
77 ParentNode: parentNode,
78 Exclusive: exclusive,
79 Path: path,
80 FullPath: fullPath,
81 Callbacks: callbacks,
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040082 }
83 return p
84}
85
Stephane Barbariedc5022d2018-11-19 15:21:44 -050086// GetRoot returns the root attribute of the proxy
87func (p *Proxy) GetRoot() *root {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050088 return p.Root
89}
90
91// getPath returns the path attribute of the proxy
92func (p *Proxy) getPath() string {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050093 return p.Path
94}
95
96// getFullPath returns the full path attribute of the proxy
97func (p *Proxy) getFullPath() string {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050098 return p.FullPath
99}
100
101// getCallbacks returns the full list of callbacks associated to the proxy
102func (p *Proxy) getCallbacks(callbackType CallbackType) map[string]*CallbackTuple {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500103 if cb, exists := p.Callbacks[callbackType]; exists {
104 return cb
105 }
106 return nil
107}
108
109// getCallback returns a specific callback matching the type and function hash
110func (p *Proxy) getCallback(callbackType CallbackType, funcHash string) *CallbackTuple {
Stephane Barbarie260a5632019-02-26 16:12:49 -0500111 p.Lock()
112 defer p.Unlock()
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500113 if tuple, exists := p.Callbacks[callbackType][funcHash]; exists {
114 return tuple
115 }
116 return nil
117}
118
119// setCallbacks applies a callbacks list to a type
120func (p *Proxy) setCallbacks(callbackType CallbackType, callbacks map[string]*CallbackTuple) {
121 p.Lock()
122 defer p.Unlock()
123 p.Callbacks[callbackType] = callbacks
124}
125
126// setCallback applies a callback to a type and hash value
127func (p *Proxy) setCallback(callbackType CallbackType, funcHash string, tuple *CallbackTuple) {
128 p.Lock()
129 defer p.Unlock()
130 p.Callbacks[callbackType][funcHash] = tuple
131}
132
133// DeleteCallback removes a callback matching the type and hash
134func (p *Proxy) DeleteCallback(callbackType CallbackType, funcHash string) {
135 p.Lock()
136 defer p.Unlock()
137 delete(p.Callbacks[callbackType], funcHash)
138}
139
Stephane Barbariec53a2752019-03-08 17:50:10 -0500140// CallbackType is an enumerated value to express when a callback should be executed
141type ProxyOperation uint8
142
143// Enumerated list of callback types
144const (
145 PROXY_GET ProxyOperation = iota
146 PROXY_LIST
147 PROXY_ADD
148 PROXY_UPDATE
149 PROXY_REMOVE
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400150 PROXY_CREATE
Stephane Barbariec53a2752019-03-08 17:50:10 -0500151)
152
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500153// parseForControlledPath verifies if a proxy path matches a pattern
154// for locations that need to be access controlled.
Stephane Barbariea188d942018-10-16 16:43:04 -0400155func (p *Proxy) parseForControlledPath(path string) (pathLock string, controlled bool) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500156 // TODO: Add other path prefixes that may need control
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500157 if strings.HasPrefix(path, "/devices") ||
158 strings.HasPrefix(path, "/logical_devices") ||
159 strings.HasPrefix(path, "/adapters") {
160
Stephane Barbariea188d942018-10-16 16:43:04 -0400161 split := strings.SplitN(path, "/", -1)
162 switch len(split) {
163 case 2:
164 controlled = false
165 pathLock = ""
166 break
167 case 3:
168 fallthrough
169 default:
170 pathLock = fmt.Sprintf("%s/%s", split[1], split[2])
171 controlled = true
172 }
173 }
174 return pathLock, controlled
175}
176
Stephane Barbarieaa467942019-02-06 14:09:44 -0500177// List will retrieve information from the data model at the specified path location
178// A list operation will force access to persistence storage
179func (p *Proxy) List(path string, depth int, deep bool, txid string) interface{} {
180 var effectivePath string
181 if path == "/" {
182 effectivePath = p.getFullPath()
183 } else {
184 effectivePath = p.getFullPath() + path
185 }
186
187 pathLock, controlled := p.parseForControlledPath(effectivePath)
188
189 log.Debugf("Path: %s, Effective: %s, PathLock: %s", path, effectivePath, pathLock)
190
191 pac := PAC().ReservePath(effectivePath, p, pathLock)
192 defer PAC().ReleasePath(pathLock)
193 pac.SetProxy(p)
194
195 rv := pac.List(path, depth, deep, txid, controlled)
196
197 return rv
198}
199
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500200// Get will retrieve information from the data model at the specified path location
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400201func (p *Proxy) Get(path string, depth int, deep bool, txid string) interface{} {
Stephane Barbariea188d942018-10-16 16:43:04 -0400202 var effectivePath string
203 if path == "/" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500204 effectivePath = p.getFullPath()
Stephane Barbariea188d942018-10-16 16:43:04 -0400205 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500206 effectivePath = p.getFullPath() + path
Stephane Barbariea188d942018-10-16 16:43:04 -0400207 }
208
209 pathLock, controlled := p.parseForControlledPath(effectivePath)
210
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500211 log.Debugf("Path: %s, Effective: %s, PathLock: %s", path, effectivePath, pathLock)
Stephane Barbariea188d942018-10-16 16:43:04 -0400212
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500213 pac := PAC().ReservePath(effectivePath, p, pathLock)
214 defer PAC().ReleasePath(pathLock)
215 pac.SetProxy(p)
Stephane Barbariea188d942018-10-16 16:43:04 -0400216
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500217 rv := pac.Get(path, depth, deep, txid, controlled)
218
219 return rv
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400220}
221
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500222// Update will modify information in the data model at the specified location with the provided data
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400223func (p *Proxy) Update(path string, data interface{}, strict bool, txid string) interface{} {
224 if !strings.HasPrefix(path, "/") {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400225 log.Errorf("invalid path: %s", path)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400226 return nil
227 }
228 var fullPath string
Stephane Barbariea188d942018-10-16 16:43:04 -0400229 var effectivePath string
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400230 if path == "/" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500231 fullPath = p.getPath()
232 effectivePath = p.getFullPath()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400233 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500234 fullPath = p.getPath() + path
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500235 effectivePath = p.getFullPath() + path
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400236 }
Stephane Barbarie126101e2018-10-11 16:18:48 -0400237
Stephane Barbariea188d942018-10-16 16:43:04 -0400238 pathLock, controlled := p.parseForControlledPath(effectivePath)
239
Stephane Barbarie260a5632019-02-26 16:12:49 -0500240 log.Debugf("Path: %s, Effective: %s, Full: %s, PathLock: %s, Controlled: %b", path, effectivePath, fullPath, pathLock, controlled)
Stephane Barbariea188d942018-10-16 16:43:04 -0400241
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500242 pac := PAC().ReservePath(effectivePath, p, pathLock)
243 defer PAC().ReleasePath(pathLock)
Stephane Barbariec53a2752019-03-08 17:50:10 -0500244
245 p.Operation = PROXY_UPDATE
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500246 pac.SetProxy(p)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400247 defer func(op ProxyOperation) {
248 pac.getProxy().Operation = op
249 }(PROXY_GET)
Stephane Barbariea188d942018-10-16 16:43:04 -0400250
Stephane Barbariec53a2752019-03-08 17:50:10 -0500251 log.Debugw("proxy-operation--update", log.Fields{"operation":p.Operation})
252
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500253 return pac.Update(fullPath, data, strict, txid, controlled)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400254}
255
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500256// AddWithID will insert new data at specified location.
257// This method also allows the user to specify the ID of the data entry to ensure
258// that access control is active while inserting the information.
259func (p *Proxy) AddWithID(path string, id string, data interface{}, txid string) interface{} {
260 if !strings.HasPrefix(path, "/") {
261 log.Errorf("invalid path: %s", path)
262 return nil
263 }
264 var fullPath string
265 var effectivePath string
266 if path == "/" {
267 fullPath = p.getPath()
268 effectivePath = p.getFullPath()
269 } else {
270 fullPath = p.getPath() + path
271 effectivePath = p.getFullPath() + path + "/" + id
272 }
273
274 pathLock, controlled := p.parseForControlledPath(effectivePath)
275
276 log.Debugf("Path: %s, Effective: %s, Full: %s, PathLock: %s", path, effectivePath, fullPath, pathLock)
277
278 pac := PAC().ReservePath(path, p, pathLock)
279 defer PAC().ReleasePath(pathLock)
Stephane Barbariec53a2752019-03-08 17:50:10 -0500280
281 p.Operation = PROXY_ADD
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400282 defer func() {
283 p.Operation = PROXY_GET
284 }()
285
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500286 pac.SetProxy(p)
287
Stephane Barbariec53a2752019-03-08 17:50:10 -0500288 log.Debugw("proxy-operation--add", log.Fields{"operation":p.Operation})
289
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500290 return pac.Add(fullPath, data, txid, controlled)
291}
292
293// Add will insert new data at specified location.
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400294func (p *Proxy) Add(path string, data interface{}, txid string) interface{} {
295 if !strings.HasPrefix(path, "/") {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400296 log.Errorf("invalid path: %s", path)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400297 return nil
298 }
299 var fullPath string
Stephane Barbariea188d942018-10-16 16:43:04 -0400300 var effectivePath string
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400301 if path == "/" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500302 fullPath = p.getPath()
303 effectivePath = p.getFullPath()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400304 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500305 fullPath = p.getPath() + path
306 effectivePath = p.getFullPath() + path
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400307 }
Stephane Barbariea188d942018-10-16 16:43:04 -0400308
309 pathLock, controlled := p.parseForControlledPath(effectivePath)
310
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500311 log.Debugf("Path: %s, Effective: %s, Full: %s, PathLock: %s", path, effectivePath, fullPath, pathLock)
Stephane Barbariea188d942018-10-16 16:43:04 -0400312
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500313 pac := PAC().ReservePath(path, p, pathLock)
314 defer PAC().ReleasePath(pathLock)
Stephane Barbariec53a2752019-03-08 17:50:10 -0500315
316 p.Operation = PROXY_ADD
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500317 pac.SetProxy(p)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400318 defer func() {
319 p.Operation = PROXY_GET
320 }()
Stephane Barbariea188d942018-10-16 16:43:04 -0400321
Stephane Barbariec53a2752019-03-08 17:50:10 -0500322 log.Debugw("proxy-operation--add", log.Fields{"operation":p.Operation})
323
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500324 return pac.Add(fullPath, data, txid, controlled)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400325}
326
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500327// Remove will delete an entry at the specified location
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400328func (p *Proxy) Remove(path string, txid string) interface{} {
329 if !strings.HasPrefix(path, "/") {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400330 log.Errorf("invalid path: %s", path)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400331 return nil
332 }
333 var fullPath string
Stephane Barbariea188d942018-10-16 16:43:04 -0400334 var effectivePath string
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400335 if path == "/" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500336 fullPath = p.getPath()
337 effectivePath = p.getFullPath()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400338 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500339 fullPath = p.getPath() + path
340 effectivePath = p.getFullPath() + path
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400341 }
Stephane Barbariea188d942018-10-16 16:43:04 -0400342
343 pathLock, controlled := p.parseForControlledPath(effectivePath)
344
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500345 log.Debugf("Path: %s, Effective: %s, Full: %s, PathLock: %s", path, effectivePath, fullPath, pathLock)
Stephane Barbariea188d942018-10-16 16:43:04 -0400346
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500347 pac := PAC().ReservePath(effectivePath, p, pathLock)
348 defer PAC().ReleasePath(pathLock)
Stephane Barbariec53a2752019-03-08 17:50:10 -0500349
350 p.Operation = PROXY_REMOVE
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500351 pac.SetProxy(p)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400352 defer func() {
353 p.Operation = PROXY_GET
354 }()
Stephane Barbariea188d942018-10-16 16:43:04 -0400355
Stephane Barbariec53a2752019-03-08 17:50:10 -0500356 log.Debugw("proxy-operation--remove", log.Fields{"operation":p.Operation})
357
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500358 return pac.Remove(fullPath, txid, controlled)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400359}
360
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400361// CreateProxy to interact with specific path directly
362func (p *Proxy) CreateProxy(path string, exclusive bool) *Proxy {
363 if !strings.HasPrefix(path, "/") {
364 log.Errorf("invalid path: %s", path)
365 return nil
366 }
367
368 var fullPath string
369 var effectivePath string
370 if path == "/" {
371 fullPath = p.getPath()
372 effectivePath = p.getFullPath()
373 } else {
374 fullPath = p.getPath() + path
375 effectivePath = p.getFullPath() + path
376 }
377
378 pathLock, controlled := p.parseForControlledPath(effectivePath)
379
380 log.Debugf("Path: %s, Effective: %s, Full: %s, PathLock: %s", path, effectivePath, fullPath, pathLock)
381
382 pac := PAC().ReservePath(path, p, pathLock)
383 defer PAC().ReleasePath(pathLock)
384
385 p.Operation = PROXY_CREATE
386 pac.SetProxy(p)
387 defer func() {
388 p.Operation = PROXY_GET
389 }()
390
391 log.Debugw("proxy-operation--create-proxy", log.Fields{"operation":p.Operation})
392
393 return pac.CreateProxy(fullPath, exclusive, controlled)
394}
395
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500396// OpenTransaction creates a new transaction branch to isolate operations made to the data model
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400397func (p *Proxy) OpenTransaction() *Transaction {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500398 txid := p.GetRoot().MakeTxBranch()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400399 return NewTransaction(p, txid)
400}
401
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500402// commitTransaction will apply and merge modifications made in the transaction branch to the data model
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400403func (p *Proxy) commitTransaction(txid string) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500404 p.GetRoot().FoldTxBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400405}
406
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500407// cancelTransaction will terminate a transaction branch along will all changes within it
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400408func (p *Proxy) cancelTransaction(txid string) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500409 p.GetRoot().DeleteTxBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400410}
411
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500412// CallbackFunction is a type used to define callback functions
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400413type CallbackFunction func(args ...interface{}) interface{}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500414
415// CallbackTuple holds the function and arguments details of a callback
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400416type CallbackTuple struct {
417 callback CallbackFunction
418 args []interface{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400419}
420
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500421// Execute will process the a callback with its provided arguments
422func (tuple *CallbackTuple) Execute(contextArgs []interface{}) interface{} {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400423 args := []interface{}{}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500424
425 for _, ta := range tuple.args {
426 args = append(args, ta)
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400427 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500428
429 if contextArgs != nil {
430 for _, ca := range contextArgs {
431 args = append(args, ca)
432 }
433 }
434
Stephane Barbarie126101e2018-10-11 16:18:48 -0400435 return tuple.callback(args...)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400436}
437
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500438// RegisterCallback associates a callback to the proxy
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400439func (p *Proxy) RegisterCallback(callbackType CallbackType, callback CallbackFunction, args ...interface{}) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500440 if p.getCallbacks(callbackType) == nil {
441 p.setCallbacks(callbackType, make(map[string]*CallbackTuple))
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400442 }
443 funcName := runtime.FuncForPC(reflect.ValueOf(callback).Pointer()).Name()
444 log.Debugf("value of function: %s", funcName)
445 funcHash := fmt.Sprintf("%x", md5.Sum([]byte(funcName)))[:12]
446
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500447 p.setCallback(callbackType, funcHash, &CallbackTuple{callback, args})
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400448}
449
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500450// UnregisterCallback removes references to a callback within a proxy
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400451func (p *Proxy) UnregisterCallback(callbackType CallbackType, callback CallbackFunction, args ...interface{}) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500452 if p.getCallbacks(callbackType) == nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400453 log.Errorf("no such callback type - %s", callbackType.String())
454 return
455 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500456
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400457 funcName := runtime.FuncForPC(reflect.ValueOf(callback).Pointer()).Name()
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400458 funcHash := fmt.Sprintf("%x", md5.Sum([]byte(funcName)))[:12]
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500459
460 log.Debugf("value of function: %s", funcName)
461
462 if p.getCallback(callbackType, funcHash) == nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400463 log.Errorf("function with hash value: '%s' not registered with callback type: '%s'", funcHash, callbackType)
464 return
465 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500466
467 p.DeleteCallback(callbackType, funcHash)
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400468}
469
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500470func (p *Proxy) invoke(callback *CallbackTuple, context []interface{}) (result interface{}, err error) {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400471 defer func() {
472 if r := recover(); r != nil {
473 errStr := fmt.Sprintf("callback error occurred: %+v", r)
474 err = errors.New(errStr)
475 log.Error(errStr)
476 }
477 }()
478
479 result = callback.Execute(context)
480
481 return result, err
482}
483
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500484// InvokeCallbacks executes all callbacks associated to a specific type
Stephane Barbarie126101e2018-10-11 16:18:48 -0400485func (p *Proxy) InvokeCallbacks(args ...interface{}) (result interface{}) {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400486 callbackType := args[0].(CallbackType)
Stephane Barbarie126101e2018-10-11 16:18:48 -0400487 proceedOnError := args[1].(bool)
488 context := args[2:]
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400489
490 var err error
491
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500492 if callbacks := p.getCallbacks(callbackType); callbacks != nil {
493 p.Lock()
494 for _, callback := range callbacks {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400495 if result, err = p.invoke(callback, context); err != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400496 if !proceedOnError {
497 log.Info("An error occurred. Stopping callback invocation")
498 break
499 }
500 log.Info("An error occurred. Invoking next callback")
501 }
502 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500503 p.Unlock()
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400504 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500505
Stephane Barbariea188d942018-10-16 16:43:04 -0400506 return result
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400507}