2024-09-28 20:18:32 +00:00
|
|
|
package framework
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"sync"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Container interface {
|
|
|
|
// Bind binds a service provider to the container.
|
|
|
|
//
|
|
|
|
// If the service exists, the old one will be replaced by the new one.
|
|
|
|
// No error is returned in this case.
|
|
|
|
//
|
|
|
|
// Return any Init() error.
|
|
|
|
Bind(provider ServiceProvider) error
|
|
|
|
|
|
|
|
// IsBound returns true the provider is bound to the container.
|
|
|
|
IsBound(name string) bool
|
|
|
|
|
2024-09-29 21:50:00 +00:00
|
|
|
// Make gets or creates a service by its name.
|
2024-09-28 20:18:32 +00:00
|
|
|
//
|
|
|
|
// Return error if the service cannot be initiated or doesn't exist.
|
2024-09-29 21:50:00 +00:00
|
|
|
Make(name string) (interface{}, error)
|
2024-09-28 20:18:32 +00:00
|
|
|
|
2024-09-29 21:50:00 +00:00
|
|
|
// MustMake makes a service, supposing that it is not instantiated before.
|
|
|
|
// Return nil if error.
|
|
|
|
MustMake(name string) interface{}
|
2024-09-28 20:18:32 +00:00
|
|
|
|
|
|
|
// MakeNew creates a new instance of the service with different parameters.
|
|
|
|
//
|
|
|
|
// Used for non singleton services.
|
|
|
|
MakeNew(name string, params []interface{}) (interface{}, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
type GoWebContainer struct {
|
|
|
|
// Must implement Container interfaces.
|
|
|
|
Container
|
|
|
|
|
|
|
|
// Service providers by name.
|
|
|
|
providers map[string]ServiceProvider
|
|
|
|
|
|
|
|
// Instantiated services by name.
|
|
|
|
instances map[string]interface{}
|
|
|
|
|
|
|
|
// RWMutex to protect the changes in container.
|
|
|
|
mu sync.RWMutex
|
|
|
|
}
|
|
|
|
|
|
|
|
func errUnknownService(name string) error {
|
|
|
|
return fmt.Errorf("service %q is not registered in the container", name)
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewGoWebContainer() *GoWebContainer {
|
|
|
|
return &GoWebContainer{
|
|
|
|
providers: map[string]ServiceProvider{},
|
|
|
|
instances: map[string]interface{}{},
|
|
|
|
mu: sync.RWMutex{},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *GoWebContainer) PrintProviders(w io.Writer) {
|
|
|
|
ret := make([]string, len(c.providers))
|
|
|
|
for _, provider := range c.providers {
|
|
|
|
name := provider.Name()
|
|
|
|
|
|
|
|
ret = append(ret, name)
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "%v", ret)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *GoWebContainer) Bind(provider ServiceProvider) error {
|
|
|
|
c.bindProvider(provider)
|
|
|
|
// Instantiate Later ?
|
|
|
|
if provider.InstantiateLater() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Instantiate now !
|
|
|
|
_, err := c.instantiate(provider, nil)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *GoWebContainer) bindProvider(provider ServiceProvider) {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
name := provider.Name()
|
|
|
|
c.providers[name] = provider
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *GoWebContainer) IsBound(name string) bool {
|
|
|
|
return c.getServiceProvider(name) != nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *GoWebContainer) getServiceProvider(name string) ServiceProvider {
|
|
|
|
c.mu.RLock()
|
|
|
|
defer c.mu.RUnlock()
|
|
|
|
|
|
|
|
provider, ok := c.providers[name]
|
|
|
|
if ok {
|
|
|
|
return provider
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-09-29 21:50:00 +00:00
|
|
|
func (c *GoWebContainer) Make(name string) (interface{}, error) {
|
2024-09-28 20:18:32 +00:00
|
|
|
return c.makeServiceInstance(name, nil, false)
|
|
|
|
}
|
|
|
|
|
2024-09-29 21:50:00 +00:00
|
|
|
func (c *GoWebContainer) MustMake(name string) interface{} {
|
2024-09-28 20:18:32 +00:00
|
|
|
ins, err := c.makeServiceInstance(name, nil, false)
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return ins
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *GoWebContainer) MakeNew(name string, params []interface{}) (interface{}, error) {
|
|
|
|
return c.makeServiceInstance(name, params, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *GoWebContainer) getServiceInstance(name string) interface{} {
|
|
|
|
c.mu.RLock()
|
|
|
|
defer c.mu.RUnlock()
|
|
|
|
|
|
|
|
instance, ok := c.instances[name]
|
|
|
|
if ok {
|
|
|
|
return instance
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *GoWebContainer) makeServiceInstance(
|
|
|
|
name string,
|
|
|
|
params []interface{},
|
|
|
|
forceNew bool,
|
|
|
|
) (interface{}, error) {
|
|
|
|
provider := c.getServiceProvider(name)
|
|
|
|
if provider == nil {
|
|
|
|
return nil, errUnknownService(name)
|
|
|
|
}
|
|
|
|
|
|
|
|
if forceNew {
|
|
|
|
return c.instantiate(provider, params)
|
|
|
|
}
|
|
|
|
|
|
|
|
instance := c.getServiceInstance(name)
|
|
|
|
|
|
|
|
if instance != nil {
|
|
|
|
return instance, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// create a new instance
|
|
|
|
return c.instantiate(provider, params)
|
|
|
|
}
|
|
|
|
|
|
|
|
// instantiate instantiates a new instance.
|
|
|
|
// If no params are provided, then default params are used.
|
|
|
|
func (c *GoWebContainer) instantiate(
|
|
|
|
provider ServiceProvider,
|
|
|
|
params []interface{},
|
|
|
|
) (interface{}, error) {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
if err := provider.Init(c); err != nil {
|
|
|
|
// TODO: Error should be wrapped.
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if params == nil {
|
|
|
|
params = provider.Params(c)
|
|
|
|
}
|
|
|
|
|
|
|
|
construct := provider.Register(c)
|
|
|
|
instance, err := construct(params...)
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Error should be wrapped.
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
c.instances[provider.Name()] = instance
|
|
|
|
|
|
|
|
return instance, nil
|
|
|
|
}
|