github.com/vmware/transport-go@v1.3.4/bus/channel_manager.go (about)

     1  // Copyright 2019-2020 VMware, Inc.
     2  // SPDX-License-Identifier: BSD-2-Clause
     3  
     4  package bus
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"github.com/google/uuid"
    10  	"github.com/vmware/transport-go/bridge"
    11  	"github.com/vmware/transport-go/model"
    12  	"sync"
    13  )
    14  
    15  // ChannelManager interfaces controls all access to channels vis the bus.
    16  type ChannelManager interface {
    17  	CreateChannel(channelName string) *Channel
    18  	DestroyChannel(channelName string)
    19  	CheckChannelExists(channelName string) bool
    20  	GetChannel(channelName string) (*Channel, error)
    21  	GetAllChannels() map[string]*Channel
    22  	SubscribeChannelHandler(channelName string, fn MessageHandlerFunction, runOnce bool) (*uuid.UUID, error)
    23  	UnsubscribeChannelHandler(channelName string, id *uuid.UUID) error
    24  	WaitForChannel(channelName string) error
    25  	MarkChannelAsGalactic(channelName string, brokerDestination string, connection bridge.Connection) (err error)
    26  	MarkChannelAsLocal(channelName string) (err error)
    27  }
    28  
    29  func NewBusChannelManager(bus EventBus) ChannelManager {
    30  	manager := new(busChannelManager)
    31  	manager.Channels = make(map[string]*Channel)
    32  	manager.bus = bus.(*transportEventBus)
    33  	return manager
    34  }
    35  
    36  type busChannelManager struct {
    37  	Channels map[string]*Channel
    38  	bus      *transportEventBus
    39  	lock     sync.RWMutex
    40  }
    41  
    42  // Create a new Channel with the supplied Channel name. Returns pointer to new Channel object
    43  func (manager *busChannelManager) CreateChannel(channelName string) *Channel {
    44  	manager.lock.Lock()
    45  	defer manager.lock.Unlock()
    46  
    47  	channel, ok := manager.Channels[channelName]
    48  	if ok {
    49  		return channel
    50  	}
    51  
    52  	manager.Channels[channelName] = NewChannel(channelName)
    53  	go manager.bus.SendMonitorEvent(ChannelCreatedEvt, channelName, nil)
    54  	return manager.Channels[channelName]
    55  }
    56  
    57  // Destroy a Channel and all the handlers listening on it.
    58  func (manager *busChannelManager) DestroyChannel(channelName string) {
    59  	manager.lock.Lock()
    60  	defer manager.lock.Unlock()
    61  
    62  	delete(manager.Channels, channelName)
    63  	go manager.bus.SendMonitorEvent(ChannelDestroyedEvt, channelName, nil)
    64  }
    65  
    66  // Get a pointer to a Channel by name. Returns points, or error if no Channel is found.
    67  func (manager *busChannelManager) GetChannel(channelName string) (*Channel, error) {
    68  	manager.lock.RLock()
    69  	defer manager.lock.RUnlock()
    70  
    71  	if channel, ok := manager.Channels[channelName]; ok {
    72  		return channel, nil
    73  	} else {
    74  		return nil, errors.New("Channel does not exist: " + channelName)
    75  	}
    76  }
    77  
    78  // Get all channels currently open. Returns a map of Channel names and pointers to those Channel objects.
    79  func (manager *busChannelManager) GetAllChannels() map[string]*Channel {
    80  	return manager.Channels
    81  }
    82  
    83  // Check Channel exists, returns true if so.
    84  func (manager *busChannelManager) CheckChannelExists(channelName string) bool {
    85  	manager.lock.RLock()
    86  	defer manager.lock.RUnlock()
    87  
    88  	return manager.Channels[channelName] != nil
    89  }
    90  
    91  // Subscribe new handler lambda for Channel, bool flag runOnce determines if this is a single Fire handler.
    92  // Returns UUID pointer, or error if there is no Channel by that name.
    93  func (manager *busChannelManager) SubscribeChannelHandler(channelName string, fn MessageHandlerFunction, runOnce bool) (*uuid.UUID, error) {
    94  	channel, err := manager.GetChannel(channelName)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  	id := uuid.New()
    99  	channel.subscribeHandler(&channelEventHandler{callBackFunction: fn, runOnce: runOnce, uuid: &id})
   100  	manager.bus.SendMonitorEvent(ChannelSubscriberJoinedEvt, channelName, nil)
   101  	return &id, nil
   102  }
   103  
   104  // Unsubscribe a handler for a Channel event handler.
   105  func (manager *busChannelManager) UnsubscribeChannelHandler(channelName string, uuid *uuid.UUID) error {
   106  	channel, err := manager.GetChannel(channelName)
   107  	if err != nil {
   108  		return err
   109  	}
   110  	found := channel.unsubscribeHandler(uuid)
   111  	if !found {
   112  		return fmt.Errorf("no handler in Channel '%s' for uuid [%s]", channelName, uuid)
   113  	}
   114  	manager.bus.SendMonitorEvent(ChannelSubscriberLeftEvt, channelName, nil)
   115  	return nil
   116  }
   117  
   118  func (manager *busChannelManager) WaitForChannel(channelName string) error {
   119  	channel, _ := manager.GetChannel(channelName)
   120  	if channel == nil {
   121  		return fmt.Errorf("no such Channel as '%s'", channelName)
   122  	}
   123  	channel.wg.Wait()
   124  	return nil
   125  }
   126  
   127  // Mark a channel as Galactic. This will map this channel to the supplied broker destination, if the broker connector
   128  // is active and connected, this will result in a subscription to the broker destination being created. Returns
   129  // an error if the channel does not exist.
   130  func (manager *busChannelManager) MarkChannelAsGalactic(channelName string, dest string, conn bridge.Connection) (err error) {
   131  	channel, err := manager.GetChannel(channelName)
   132  	if err != nil {
   133  		return
   134  	}
   135  
   136  	// mark as galactic/
   137  	channel.SetGalactic(dest)
   138  
   139  	// create a galactic event
   140  	pl := &galacticEvent{conn: conn, dest: dest}
   141  
   142  	manager.handleGalacticChannelEvent(channelName, pl)
   143  	return nil
   144  }
   145  
   146  // Mark a channel as Local. This will unmap the channel from the broker destination, and perform an unsubscribe
   147  // operation if the broker connector is active and connected. Returns an error if the channel does not exist.
   148  func (manager *busChannelManager) MarkChannelAsLocal(channelName string) (err error) {
   149  	channel, err := manager.GetChannel(channelName)
   150  	if err != nil {
   151  		return
   152  	}
   153  	channel.SetLocal()
   154  
   155  	// get rid of all broker connections.
   156  	channel.removeBrokerConnections()
   157  
   158  	manager.handleLocalChannelEvent(channelName)
   159  
   160  	return nil
   161  }
   162  
   163  func (manager *busChannelManager) handleGalacticChannelEvent(channelName string, ge *galacticEvent) {
   164  	ch, _ := manager.GetChannel(channelName)
   165  
   166  	if ge.conn == nil {
   167  		return
   168  	}
   169  
   170  	// check if channel is already subscribed on this connection
   171  	if !ch.isBrokerSubscribedToDestination(ge.conn, ge.dest) {
   172  		if sub, e := ge.conn.Subscribe(ge.dest); e == nil {
   173  
   174  			// add broker connection to channel.
   175  			ch.addBrokerConnection(ge.conn)
   176  
   177  			m := model.GenerateResponse(&model.MessageConfig{Payload: ge.dest}) // set the mapped destination as the payload
   178  			ch.addBrokerSubscription(ge.conn, sub)
   179  			manager.bus.SendMonitorEvent(BrokerSubscribedEvt, channelName, m)
   180  			select {
   181  			case ch.brokerMappedEvent <- true: // let channel watcher know, the channel is mapped
   182  			default: // if no-one is listening, drop.
   183  			}
   184  		}
   185  	}
   186  }
   187  
   188  func (manager *busChannelManager) handleLocalChannelEvent(channelName string) {
   189  	ch, _ := manager.GetChannel(channelName)
   190  	// loop through all the connections we have mapped, and subscribe!
   191  	for _, s := range ch.brokerSubs {
   192  		if e := s.s.Unsubscribe(); e == nil {
   193  			ch.removeBrokerSubscription(s.s)
   194  			m := model.GenerateResponse(&model.MessageConfig{Payload: s.s.GetDestination()}) // set the unmapped destination as the payload
   195  			manager.bus.SendMonitorEvent(BrokerUnsubscribedEvt, channelName, m)
   196  			select {
   197  			case ch.brokerMappedEvent <- false: // let channel watcher know, the channel is un-mapped
   198  			default: // if no-one is listening, drop.
   199  			}
   200  		}
   201  	}
   202  	// get rid of all broker subscriptions on this channel.
   203  	ch.removeBrokerConnections()
   204  }
   205  
   206  type galacticEvent struct {
   207  	conn bridge.Connection
   208  	dest string
   209  }