go-web/framework/container.go

190 lines
4.0 KiB
Go
Raw Normal View History

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.
//
// 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-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{}
// 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) {
return c.makeServiceInstance(name, nil, false)
}
2024-09-29 21:50:00 +00:00
func (c *GoWebContainer) MustMake(name string) interface{} {
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
}