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 := &notifierSubscriber{
    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  }