github.com/vmware/transport-go@v1.3.4/bus/store_manager.go (about) 1 // Copyright 2019-2020 VMware, Inc. 2 // SPDX-License-Identifier: BSD-2-Clause 3 4 package bus 5 6 import ( 7 "fmt" 8 "github.com/go-stomp/stomp/v3/frame" 9 "github.com/google/uuid" 10 "github.com/vmware/transport-go/bridge" 11 "reflect" 12 "strings" 13 "sync" 14 ) 15 16 // StoreManager interface controls all access to BusStores 17 type StoreManager interface { 18 // Create a new Store, if the store already exists, then it will be returned. 19 CreateStore(name string) BusStore 20 // Create a new Store and use the itemType to deserialize item values when handling 21 // incoming UpdateStoreRequest. If the store already exists, the method will return 22 // the existing store instance. 23 CreateStoreWithType(name string, itemType reflect.Type) BusStore 24 // Get a reference to the existing store. Returns nil if the store doesn't exist. 25 GetStore(name string) BusStore 26 // Deletes a store. 27 DestroyStore(name string) bool 28 // Configure galactic store sync channel for a given connection. 29 // Should be called before OpenGalacticStore() and OpenGalacticStoreWithItemType() APIs. 30 ConfigureStoreSyncChannel(conn bridge.Connection, topicPrefix string, pubPrefix string) error 31 // Open new galactic store 32 OpenGalacticStore(name string, conn bridge.Connection) (BusStore, error) 33 // Open new galactic store and deserialize items from server to itemType 34 OpenGalacticStoreWithItemType(name string, conn bridge.Connection, itemType reflect.Type) (BusStore, error) 35 } 36 37 // Interface which is a subset of the bridge.Connection methods. 38 // Used to mock connection objects during unit testing. 39 type galacticStoreConnection interface { 40 SendJSONMessage(destination string, payload []byte, opts ...func(frame *frame.Frame) error) error 41 SendMessage(destination, contentType string, payload []byte, opts ...func(frame *frame.Frame) error) error 42 } 43 44 type storeSyncChannelConfig struct { 45 topicPrefix string 46 pubPrefix string 47 syncChannelName string 48 conn galacticStoreConnection 49 } 50 51 type storeManager struct { 52 stores map[string]BusStore 53 storesLock sync.RWMutex 54 eventBus EventBus 55 syncChannelsLock sync.RWMutex 56 syncChannels map[uuid.UUID]*storeSyncChannelConfig 57 } 58 59 func newStoreManager(eventBus EventBus) StoreManager { 60 m := new(storeManager) 61 m.stores = make(map[string]BusStore) 62 m.syncChannels = make(map[uuid.UUID]*storeSyncChannelConfig) 63 m.eventBus = eventBus 64 65 return m 66 } 67 68 func (m *storeManager) CreateStore(name string) BusStore { 69 return m.CreateStoreWithType(name, nil) 70 } 71 72 func (m *storeManager) CreateStoreWithType(name string, itemType reflect.Type) BusStore { 73 m.storesLock.Lock() 74 defer m.storesLock.Unlock() 75 76 store, ok := m.stores[name] 77 78 if ok { 79 return store 80 } 81 82 m.stores[name] = newBusStore(name, m.eventBus, itemType, nil) 83 go m.eventBus.SendMonitorEvent(StoreCreatedEvt, name, nil) 84 return m.stores[name] 85 } 86 87 func (m *storeManager) GetStore(name string) BusStore { 88 m.storesLock.RLock() 89 defer m.storesLock.RUnlock() 90 91 return m.stores[name] 92 } 93 94 func (m *storeManager) DestroyStore(name string) bool { 95 m.storesLock.Lock() 96 defer m.storesLock.Unlock() 97 98 store, ok := m.stores[name] 99 if ok { 100 store.(*busStore).OnDestroy() 101 delete(m.stores, name) 102 103 go m.eventBus.SendMonitorEvent(StoreDestroyedEvt, name, nil) 104 } 105 return ok 106 } 107 108 func (m *storeManager) ConfigureStoreSyncChannel( 109 conn bridge.Connection, topicPrefix string, pubPrefix string) error { 110 111 m.syncChannelsLock.Lock() 112 defer m.syncChannelsLock.Unlock() 113 114 _, ok := m.syncChannels[*conn.GetId()] 115 if ok { 116 return fmt.Errorf("store sync channel already configured for this connection") 117 } 118 119 if !strings.HasSuffix(topicPrefix, "/") { 120 topicPrefix += "/" 121 } 122 if !strings.HasSuffix(pubPrefix, "/") { 123 pubPrefix += "/" 124 } 125 126 syncChannel := "transport-store-sync." + conn.GetId().String() 127 128 storeSyncChannelConfig := &storeSyncChannelConfig{ 129 topicPrefix: topicPrefix, 130 pubPrefix: pubPrefix, 131 syncChannelName: syncChannel, 132 conn: conn, 133 } 134 m.syncChannels[*conn.GetId()] = storeSyncChannelConfig 135 136 m.eventBus.GetChannelManager().CreateChannel(syncChannel) 137 m.eventBus.GetChannelManager().MarkChannelAsGalactic(syncChannel, topicPrefix+syncChannel, conn) 138 139 return nil 140 } 141 142 func (m *storeManager) OpenGalacticStore(name string, conn bridge.Connection) (BusStore, error) { 143 return m.OpenGalacticStoreWithItemType(name, conn, nil) 144 } 145 146 func (m *storeManager) OpenGalacticStoreWithItemType( 147 name string, conn bridge.Connection, itemType reflect.Type) (BusStore, error) { 148 149 m.syncChannelsLock.RLock() 150 chanConf, ok := m.syncChannels[*conn.GetId()] 151 m.syncChannelsLock.RUnlock() 152 153 if !ok { 154 return nil, fmt.Errorf("sync channel is not configured for this connection") 155 } 156 157 m.storesLock.Lock() 158 defer m.storesLock.Unlock() 159 160 store, ok := m.stores[name] 161 162 if ok { 163 if store.IsGalactic() { 164 return store, nil 165 } else { 166 return store, fmt.Errorf("cannot open galactic store: there is a local store with the same name") 167 } 168 } 169 170 m.stores[name] = newBusStore(name, m.eventBus, itemType, &galacticStoreConfig{ 171 syncChannelConfig: chanConf, 172 }) 173 go m.eventBus.SendMonitorEvent(StoreCreatedEvt, name, nil) 174 return m.stores[name], nil 175 }