github.com/vmware/transport-go@v1.3.4/service/service_registry.go (about)

     1  // Copyright 2019-2021 VMware, Inc.
     2  // SPDX-License-Identifier: BSD-2-Clause
     3  
     4  package service
     5  
     6  import (
     7  	"fmt"
     8  	"github.com/vmware/transport-go/bus"
     9  	"github.com/vmware/transport-go/model"
    10  	"log"
    11  	"reflect"
    12  	"sync"
    13  )
    14  
    15  var internalServices = map[string]bool{
    16  	"fabric-rest": true,
    17  }
    18  
    19  const (
    20  	LifecycleManagerChannelName = bus.TRANSPORT_INTERNAL_CHANNEL_PREFIX + "service-lifecycle-manager"
    21  
    22  	// store constants
    23  	ServiceReadyStore      = "service-ready-notification-store"
    24  	ServiceInitStateChange = "service-init-state-change"
    25  )
    26  
    27  // ServiceRegistry is the registry for  all local fabric services.
    28  type ServiceRegistry interface {
    29  	// GetAllServiceChannels returns all active Fabric service channels as a slice of strings
    30  	GetAllServiceChannels() []string
    31  
    32  	// RegisterService registers a new fabric service and associates it with a given EventBus channel.
    33  	// Only one fabric service can be associated with a given channel.
    34  	// If the fabric service implements the FabricInitializableService interface
    35  	// its Init method will be called during the registration process.
    36  	RegisterService(service FabricService, serviceChannelName string) error
    37  
    38  	// UnregisterService unregisters the fabric service associated with the given channel.
    39  	UnregisterService(serviceChannelName string) error
    40  
    41  	// SetGlobalRestServiceBaseHost sets the global base host or host:port to be used by the restService
    42  	SetGlobalRestServiceBaseHost(host string)
    43  
    44  	// GetService returns the FabricService for the channel name given as the parameter
    45  	GetService(serviceChannelName string) (FabricService, error)
    46  }
    47  
    48  type serviceRegistry struct {
    49  	lock             sync.Mutex
    50  	services         map[string]*fabricServiceWrapper
    51  	bus              bus.EventBus
    52  	lifecycleManager *serviceLifecycleManager
    53  }
    54  
    55  var once sync.Once
    56  var registry ServiceRegistry
    57  
    58  // ResetServiceRegistry destroys existing service registry instance and creates a new one
    59  func ResetServiceRegistry() ServiceRegistry {
    60  	once = sync.Once{}
    61  	return GetServiceRegistry()
    62  }
    63  
    64  func GetServiceRegistry() ServiceRegistry {
    65  	once.Do(func() {
    66  		registry = newServiceRegistry(bus.GetBus())
    67  
    68  		// ensure a new service lifecycle manager is initialized as early as possible
    69  		svcLifecycleManagerInstance = nil
    70  		GetServiceLifecycleManager()
    71  	})
    72  	return registry
    73  }
    74  
    75  func newServiceRegistry(bus bus.EventBus) ServiceRegistry {
    76  	registry := &serviceRegistry{
    77  		bus:      bus,
    78  		services: make(map[string]*fabricServiceWrapper),
    79  	}
    80  	// create a channel for service lifecycle manager
    81  	_ = bus.GetChannelManager().CreateChannel(LifecycleManagerChannelName)
    82  
    83  	// create a bus store for delivering service ready notifications
    84  	bus.GetStoreManager().CreateStoreWithType(ServiceReadyStore, reflect.TypeOf(true)).Initialize()
    85  
    86  	// auto-register the restService
    87  	registry.RegisterService(&restService{}, restServiceChannel)
    88  	return registry
    89  }
    90  
    91  // GetService returns the FabricService instance registered at the provided service channel name.
    92  // if no service is found at the service channel it returns an error.
    93  func (r *serviceRegistry) GetService(serviceChannelName string) (FabricService, error) {
    94  	if serviceWrapper, ok := r.services[serviceChannelName]; ok {
    95  		return serviceWrapper.service, nil
    96  	}
    97  	return nil, fmt.Errorf("fabric service not found at channel %s", serviceChannelName)
    98  }
    99  
   100  func (r *serviceRegistry) SetGlobalRestServiceBaseHost(host string) {
   101  	r.services[restServiceChannel].service.(*restService).setBaseHost(host)
   102  }
   103  
   104  // GetAllServiceChannels returns the list of service channels that are registered with the registry
   105  func (r *serviceRegistry) GetAllServiceChannels() []string {
   106  	services := make([]string, 0)
   107  	for chanName, _ := range r.services {
   108  		// do not return internal services like fabric-rest
   109  		if isInternal, found := internalServices[chanName]; !isInternal || !found {
   110  			services = append(services, chanName)
   111  		}
   112  	}
   113  	return services
   114  }
   115  
   116  func (r *serviceRegistry) RegisterService(service FabricService, serviceChannelName string) error {
   117  	r.lock.Lock()
   118  	defer r.lock.Unlock()
   119  
   120  	if service == nil {
   121  		return fmt.Errorf("unable to register service: nil service")
   122  	}
   123  
   124  	if _, ok := r.services[serviceChannelName]; ok {
   125  		return fmt.Errorf("unable to register service: service channel name is already used: %s", serviceChannelName)
   126  	}
   127  
   128  	sw := newServiceWrapper(r.bus, service, serviceChannelName)
   129  	err := sw.init()
   130  	if err != nil {
   131  		return err
   132  	}
   133  
   134  	r.services[serviceChannelName] = sw
   135  
   136  	// if the service is an internal service like fabric-rest don't bother setting up lifecycle hooks
   137  	if isInternal, _ := internalServices[serviceChannelName]; isInternal {
   138  		return nil
   139  	}
   140  
   141  	// see if the service implements ServiceLifecycleHookEnabled interface and set up REST bridges as configured
   142  	var hooks ServiceLifecycleHookEnabled
   143  	lcm := GetServiceLifecycleManager()
   144  
   145  	// NOTE: this condition is only to be used when unit testing where each test case
   146  	// creates a new instance of ServiceRegistry and ServiceLifecycleManager by utility functions.
   147  	// in such cases GetServiceHooks() is not able to locate the proper test instances of those
   148  	// structs so manual wiring is needed here. again, this would / should never be used in production
   149  	// hence a private struct property.
   150  	if r.lifecycleManager != nil {
   151  		lcm = r.lifecycleManager
   152  	}
   153  
   154  	// hand off registering REST bridges to Plank via bus messages
   155  	if hooks = lcm.GetServiceHooks(serviceChannelName); hooks != nil {
   156  		if err = bus.GetBus().SendResponseMessage(
   157  			LifecycleManagerChannelName,
   158  			&SetupRESTBridgeRequest{ServiceChannel: serviceChannelName, Config: hooks.GetRESTBridgeConfig()},
   159  			bus.GetBus().GetId()); err != nil {
   160  			return err
   161  		}
   162  	}
   163  
   164  	return nil
   165  }
   166  
   167  func (r *serviceRegistry) UnregisterService(serviceChannelName string) error {
   168  	r.lock.Lock()
   169  	defer r.lock.Unlock()
   170  	sw, ok := r.services[serviceChannelName]
   171  	if !ok {
   172  		return fmt.Errorf("unable to unregister service: no service is registered for channel \"%s\"", serviceChannelName)
   173  	}
   174  	sw.unregister()
   175  	delete(r.services, serviceChannelName)
   176  	return nil
   177  }
   178  
   179  type fabricServiceWrapper struct {
   180  	service           FabricService
   181  	fabricCore        *fabricCore
   182  	requestMsgHandler bus.MessageHandler
   183  }
   184  
   185  func newServiceWrapper(
   186  	bus bus.EventBus, service FabricService, serviceChannelName string) *fabricServiceWrapper {
   187  
   188  	return &fabricServiceWrapper{
   189  		service: service,
   190  		fabricCore: &fabricCore{
   191  			bus:         bus,
   192  			channelName: serviceChannelName,
   193  		},
   194  	}
   195  }
   196  
   197  func (sw *fabricServiceWrapper) init() error {
   198  	sw.fabricCore.bus.GetChannelManager().CreateChannel(sw.fabricCore.channelName)
   199  
   200  	initializationService, ok := sw.service.(FabricInitializableService)
   201  	if ok {
   202  		initializationErr := initializationService.Init(sw.fabricCore)
   203  		if initializationErr != nil {
   204  			return initializationErr
   205  		}
   206  	}
   207  
   208  	mh, err := sw.fabricCore.bus.ListenRequestStream(sw.fabricCore.channelName)
   209  	if err != nil {
   210  		return err
   211  	}
   212  
   213  	sw.requestMsgHandler = mh
   214  	mh.Handle(
   215  		func(message *model.Message) {
   216  			requestPtr, ok := message.Payload.(*model.Request)
   217  			if !ok {
   218  				request, ok := message.Payload.(model.Request)
   219  				if !ok {
   220  					log.Println("cannot cast service request payload to model.Request")
   221  					return
   222  				}
   223  				requestPtr = &request
   224  			}
   225  
   226  			if message.DestinationId != nil {
   227  				requestPtr.Id = message.DestinationId
   228  			}
   229  
   230  			sw.service.HandleServiceRequest(requestPtr, sw.fabricCore)
   231  		},
   232  		func(e error) {})
   233  
   234  	return nil
   235  }
   236  
   237  func (sw *fabricServiceWrapper) unregister() {
   238  	if sw.requestMsgHandler != nil {
   239  		sw.requestMsgHandler.Close()
   240  	}
   241  }