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  }