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 }