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 // Make gets or creates a service by its name. // // Return error if the service cannot be initiated or doesn't exist. Make(name string) (interface{}, error) // 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 } func (c *GoWebContainer) Make(name string) (interface{}, error) { return c.makeServiceInstance(name, nil, false) } 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 }