github.com/Axway/agent-sdk@v1.1.101/pkg/notification/notifier.go (about) 1 package notification 2 3 import ( 4 "fmt" 5 "sync" 6 7 guuid "github.com/google/uuid" 8 9 log "github.com/Axway/agent-sdk/pkg/util/log" 10 ) 11 12 var notifiers map[string]Notifier 13 var pubLock = &sync.RWMutex{} // Lock used when reading/modifying notifiers map 14 15 // Notifier - any channel that has the potential to have listeners (1-n) 16 type Notifier interface { 17 GetName() string 18 Stop() 19 Subscribe(Subscriber) 20 Unsubscribe(string) error 21 Start() 22 } 23 24 type channelNotifier struct { 25 Notifier 26 name string 27 source chan interface{} 28 subscribers map[string]Subscriber 29 subLock *sync.RWMutex // Lock used when reading/modifying subscribers map 30 endNotifier chan struct{} 31 } 32 33 func init() { 34 notifiers = make(map[string]Notifier) 35 } 36 37 // RegisterNotifier - accepts a name and source channel to make a new notifier object 38 func RegisterNotifier(name string, source chan interface{}) (Notifier, error) { 39 pubLock.Lock() // Adding a notifier 40 defer pubLock.Unlock() 41 if _, ok := notifiers[name]; ok { 42 return nil, fmt.Errorf("A notifier with the name %s already exists", name) 43 } 44 45 notifiers[name] = &channelNotifier{ 46 name: name, 47 source: source, 48 subscribers: make(map[string]Subscriber), 49 subLock: &sync.RWMutex{}, 50 endNotifier: make(chan struct{}), 51 } 52 return notifiers[name], nil 53 } 54 55 // Subscribe - subscribes to events on the notifier sending them to the output channel. 56 func Subscribe(name string, output chan interface{}) (Subscriber, error) { 57 pubLock.RLock() // reading the notifiers 58 defer pubLock.RUnlock() 59 if notifier, ok := notifiers[name]; ok { 60 id := guuid.New().String() 61 subscriber := ¬ifierSubscriber{ 62 id: id, 63 notifier: name, 64 output: output, 65 } 66 notifier.Subscribe(subscriber) 67 return subscriber, nil 68 } 69 70 return nil, fmt.Errorf("Could not find notifier %s to subscribe to", name) 71 } 72 73 // Unsubscribe - removes the subscriber, indicated by id, from the notifier, indicated by name 74 func Unsubscribe(name string, id string) error { 75 pubLock.RLock() // reading the notifiers 76 defer pubLock.RUnlock() 77 if notifier, ok := notifiers[name]; ok { 78 return notifier.Unsubscribe(id) 79 } 80 81 return fmt.Errorf("Could not find notifier %s to unsubscribe from", name) 82 } 83 84 // GetName - returns the friendly name of this notifier 85 func (s *channelNotifier) GetName() string { 86 return s.name 87 } 88 89 // safeCloseChannel - Recovers from panic on close of closed channel 90 func safeCloseChannel(ch chan struct{}) (closedRes bool) { 91 defer func() { 92 if recover() != nil { 93 // The return result can be altered 94 // in a defer function call. 95 closedRes = false 96 } 97 }() 98 close(ch) 99 return true 100 } 101 102 // Stop - closes all subscribers and stops the subscription loop 103 func (s *channelNotifier) Stop() { 104 safeCloseChannel(s.endNotifier) 105 pubLock.Lock() // removing a notifier 106 defer pubLock.Unlock() 107 delete(notifiers, s.GetName()) 108 } 109 110 // Subscribe - adds a subscriber to the subscribers array 111 func (s *channelNotifier) Subscribe(newSub Subscriber) { 112 s.subscribe(newSub) 113 } 114 115 // adds a subscriber to the subscribers array 116 func (s *channelNotifier) subscribe(newSub Subscriber) { 117 s.subLock.Lock() // Adding a subscriber 118 defer s.subLock.Unlock() 119 s.subscribers[newSub.GetID()] = newSub 120 } 121 122 // Unsubscribe - remove the subscriber identified with id from the notifier list 123 func (s *channelNotifier) Unsubscribe(id string) error { 124 return s.unsubscribe(id) 125 } 126 127 // remove the subscriber identified with id from the notifier list 128 func (s *channelNotifier) unsubscribe(id string) error { 129 s.subLock.Lock() // Removing a dubscriber 130 defer s.subLock.Unlock() 131 if sub, ok := s.subscribers[id]; ok { 132 delete(s.subscribers, id) 133 sub.close() 134 return nil 135 } 136 return fmt.Errorf("Could not find subscriber with id: %s", id) 137 } 138 139 func (s *channelNotifier) unsubscribeAll() { 140 // sends messages to all of the subscribers 141 for _, sub := range s.subscribers { 142 s.unsubscribe(sub.GetID()) 143 } 144 } 145 146 func (s *channelNotifier) sendMsgs(msg interface{}) { 147 // sends messages to all of the subscribers 148 s.subLock.RLock() // reading the subscribers map 149 defer s.subLock.RUnlock() 150 for _, sub := range s.subscribers { 151 sub.SendMsg(msg) 152 } 153 } 154 155 // Start - starts a go routine with the infinite loop waiting for messages 156 func (s *channelNotifier) Start() { 157 go s.start() 158 } 159 160 // infinite loop waiting for messags on the source channel and sending to the subscribers 161 func (s *channelNotifier) start() { 162 for { 163 select { 164 case <-s.endNotifier: // notifier is closing, close all of its subscribers 165 log.Debugf("Received close for notifier %s", s.GetName()) 166 s.unsubscribeAll() 167 close(s.source) 168 return 169 case msg, ok := <-s.source: // message received, send to all of it's subscribers 170 if ok { 171 log.Debugf("Received message for notifier %s: sending to %d subscribers", s.GetName(), len(s.subscribers)) 172 s.sendMsgs(msg) 173 } 174 } 175 } 176 }