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 }