github.com/Axway/agent-sdk@v1.1.101/pkg/cache/pubsub.go (about)

     1  package cache
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/Axway/agent-sdk/pkg/notification"
     7  	util "github.com/Axway/agent-sdk/pkg/util"
     8  )
     9  
    10  var topics map[string]*cachePubSub
    11  
    12  type cachePubSub struct {
    13  	notification.PubSub
    14  	topic    string
    15  	notifier notification.Notifier
    16  	channel  chan interface{}
    17  }
    18  
    19  func init() {
    20  	topics = make(map[string]*cachePubSub)
    21  }
    22  
    23  // CreateTopic - create a new PubSub for a cache item
    24  func CreateTopic(topic string) (notification.PubSub, error) {
    25  	return CreateTopicWithInitData(topic, nil)
    26  }
    27  
    28  // CreateTopicWithInitData - create a new PubSub with an item that will be cached, initialize it as well
    29  func CreateTopicWithInitData(topic string, initData interface{}) (notification.PubSub, error) {
    30  	_, err := globalCache.Get(topic)
    31  	if err == nil {
    32  		return nil, fmt.Errorf("could not create a PubSub topic, name in cache already used")
    33  	}
    34  	globalCache.Set(topic, initData)
    35  	channel := make(chan interface{})
    36  	notifier, err := notification.RegisterNotifier(topic, channel)
    37  	if err != nil {
    38  		return nil, fmt.Errorf("could not create a PubSub: %s", err.Error())
    39  	}
    40  	notifier.Start() // Start the notifier to listen for data on the channel
    41  	newCachePubSub := &cachePubSub{
    42  		topic:    topic,
    43  		notifier: notifier,
    44  		channel:  channel,
    45  	}
    46  	topics[topic] = newCachePubSub
    47  	return newCachePubSub, nil
    48  }
    49  
    50  // RemoveTopic - removes a PubSub topic and cleans up it's cache
    51  func RemoveTopic(topic string) error {
    52  	if _, ok := topics[topic]; !ok {
    53  		return fmt.Errorf("can't remove topic %s, this topic is unknown", topic)
    54  	}
    55  
    56  	// Clean the cache
    57  	var cacheErr error
    58  	if err := globalCache.Delete(topic); err != nil {
    59  		cacheErr = fmt.Errorf("hit error deleting the cache item for topic %s: %s", topic, err.Error())
    60  	}
    61  
    62  	// Stop the notifier and remove the topic from the topics map
    63  	topics[topic].notifier.Stop()
    64  	delete(topics, topic)
    65  	return cacheErr
    66  }
    67  
    68  // GetPubSub - find a PubSub by topic name
    69  func GetPubSub(topic string) (notification.PubSub, error) {
    70  	cPubSub, ok := topics[topic]
    71  	if !ok {
    72  		return nil, fmt.Errorf("could not find topic: %s", topic)
    73  	}
    74  
    75  	return cPubSub, nil
    76  }
    77  
    78  func (c *cachePubSub) hasChanged(key string, data interface{}) (bool, error) {
    79  	changed, err := globalCache.HasItemChanged(key, data)
    80  	if !changed && err != nil {
    81  		return false, err
    82  	}
    83  	return changed, nil
    84  }
    85  
    86  func (c *cachePubSub) updateCache(key, secondaryKey string, data interface{}) {
    87  	globalCache.Set(key, data)
    88  	if secondaryKey != "" {
    89  		globalCache.SetSecondaryKey(key, secondaryKey)
    90  	}
    91  }
    92  
    93  func (c *cachePubSub) setAndSend(key string, data interface{}) error {
    94  	return c.setAndSendWithSecondaryKey(key, "", data)
    95  }
    96  
    97  func (c *cachePubSub) setAndSendWithSecondaryKey(key, secondaryKey string, data interface{}) error {
    98  	c.updateCache(key, secondaryKey, data)
    99  	c.channel <- data
   100  	return nil
   101  }
   102  
   103  func (c *cachePubSub) setHashAndSend(key string, data interface{}, hash uint64) error {
   104  	return c.setHashAndSendWithSecondaryKey(key, "", data, hash)
   105  }
   106  
   107  func (c *cachePubSub) setHashAndSendWithSecondaryKey(key, secondaryKey string, data interface{}, hash uint64) error {
   108  	c.updateCache(key, secondaryKey, hash)
   109  	c.channel <- data
   110  	return nil
   111  }
   112  
   113  // Publish - send in data to publish, if it has changed update cache and notify subscribers
   114  func (c *cachePubSub) Publish(key, secondaryKey string, data interface{}) error {
   115  	changed, err := c.hasChanged(key, data)
   116  	if !changed || err != nil {
   117  		return err
   118  	}
   119  
   120  	// Data has changed
   121  	return c.setAndSendWithSecondaryKey(key, secondaryKey, data)
   122  }
   123  
   124  // Publish - send in data to publish, if it has changed update cache and notify subscribers
   125  func (c *cachePubSub) PublishToTopic(data interface{}) error {
   126  	changed, err := c.hasChanged(c.topic, data)
   127  	if !changed || err != nil {
   128  		return err
   129  	}
   130  
   131  	// Data has changed
   132  	return c.setAndSend(c.topic, data)
   133  }
   134  
   135  // Publish - send in data to publish, if it has changed update cache and notify subscribers
   136  func (c *cachePubSub) PublishToTopicWithSecondaryKey(secondaryKey string, data interface{}) error {
   137  	changed, err := c.hasChanged(c.topic, data)
   138  	if !changed || err != nil {
   139  		return err
   140  	}
   141  
   142  	// Data has changed
   143  	return c.setAndSendWithSecondaryKey(c.topic, secondaryKey, data)
   144  }
   145  
   146  // PublishCacheHash - send in data to publish, if it has changed update cache, storing only the hash, notify subscribers
   147  func (c *cachePubSub) PublishCacheHash(key, secondaryKey string, data interface{}) error {
   148  	hash, err := util.ComputeHash(data)
   149  	if err != nil {
   150  		return err
   151  	}
   152  
   153  	changed, err := c.hasChanged(key, hash)
   154  	if !changed || err != nil {
   155  		return err
   156  	}
   157  
   158  	// Data has changed
   159  	return c.setHashAndSendWithSecondaryKey(key, secondaryKey, data, hash)
   160  }
   161  
   162  // PublishCacheHash - send in data to publish, if it has changed update cache, storing only the hash, notify subscribers
   163  func (c *cachePubSub) PublishCacheHashToTopic(data interface{}) error {
   164  	hash, err := util.ComputeHash(data)
   165  	if err != nil {
   166  		return err
   167  	}
   168  
   169  	changed, err := c.hasChanged(c.topic, hash)
   170  	if !changed || err != nil {
   171  		return err
   172  	}
   173  
   174  	// Data has changed
   175  	return c.setHashAndSend(c.topic, data, hash)
   176  }
   177  
   178  // PublishCacheHash - send in data to publish, if it has changed update cache, storing only the hash, notify subscribers
   179  func (c *cachePubSub) PublishCacheHashToTopicWithSecondaryKey(secondaryKey string, data interface{}) error {
   180  	hash, err := util.ComputeHash(data)
   181  	if err != nil {
   182  		return err
   183  	}
   184  
   185  	changed, err := c.hasChanged(c.topic, hash)
   186  	if !changed || err != nil {
   187  		return err
   188  	}
   189  
   190  	// Data has changed
   191  	return c.setHashAndSendWithSecondaryKey(c.topic, secondaryKey, data, hash)
   192  }
   193  
   194  // Unsubscribe - stop subscriber identified by id
   195  func (c *cachePubSub) Unsubscribe(id string) error {
   196  	return c.notifier.Unsubscribe(id)
   197  }
   198  
   199  // Subscribe - creates a subscriber to this cache topic
   200  func (c *cachePubSub) Subscribe() (chan interface{}, string) {
   201  	channel := make(chan interface{})
   202  	subscriber, _ := notification.Subscribe(c.topic, channel)
   203  	return channel, subscriber.GetID()
   204  }
   205  
   206  // SubscribeWithCallback - creates a subscriber to this cache topic, when data is received the callback is called
   207  func (c *cachePubSub) SubscribeWithCallback(callback func(data interface{})) string {
   208  	channel := make(chan interface{})
   209  	subscriber, _ := notification.Subscribe(c.topic, channel)
   210  
   211  	// Start a go function looping for data, when received send to callback
   212  	go func() {
   213  		for {
   214  			msg, ok := <-channel
   215  			if ok {
   216  				callback(msg)
   217  			}
   218  		}
   219  	}()
   220  
   221  	return subscriber.GetID()
   222  }