github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/state/apiserver/common/resource.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package common
     5  
     6  import (
     7  	"fmt"
     8  	"strconv"
     9  	"sync"
    10  )
    11  
    12  // Resource represents any resource that should be cleaned up when an
    13  // API connection terminates. The Stop method will be called when
    14  // that happens.
    15  type Resource interface {
    16  	Stop() error
    17  }
    18  
    19  // Resources holds all the resources for a connection.
    20  // It allows the registration of resources that will be cleaned
    21  // up when a connection terminates.
    22  type Resources struct {
    23  	mu        sync.Mutex
    24  	maxId     uint64
    25  	resources map[string]Resource
    26  }
    27  
    28  func NewResources() *Resources {
    29  	return &Resources{
    30  		resources: make(map[string]Resource),
    31  	}
    32  }
    33  
    34  // Get returns the resource for the given id, or
    35  // nil if there is no such resource.
    36  func (rs *Resources) Get(id string) Resource {
    37  	rs.mu.Lock()
    38  	defer rs.mu.Unlock()
    39  	return rs.resources[id]
    40  }
    41  
    42  // Register registers the given resource. It returns a unique
    43  // identifier for the resource which can then be used in
    44  // subsequent API requests to refer to the resource.
    45  func (rs *Resources) Register(r Resource) string {
    46  	rs.mu.Lock()
    47  	defer rs.mu.Unlock()
    48  	rs.maxId++
    49  	id := strconv.FormatUint(rs.maxId, 10)
    50  	rs.resources[id] = r
    51  	return id
    52  }
    53  
    54  // RegisterNamed registers the given resource. Callers must supply a unique
    55  // name for the given resource. It is an error to try to register another
    56  // resource with the same name as an already registered name. (This could be
    57  // softened that you can overwrite an existing one and it will be Stopped and
    58  // replaced, but we don't have a need for that yet.)
    59  // It is also an error to supply a name that is an integer string, since that
    60  // collides with the auto-naming from Register.
    61  func (rs *Resources) RegisterNamed(name string, r Resource) error {
    62  	rs.mu.Lock()
    63  	defer rs.mu.Unlock()
    64  	if _, err := strconv.Atoi(name); err == nil {
    65  		return fmt.Errorf("RegisterNamed does not allow integer names: %q", name)
    66  	}
    67  	if _, ok := rs.resources[name]; ok {
    68  		return fmt.Errorf("resource %q already registered", name)
    69  	}
    70  	rs.resources[name] = r
    71  	return nil
    72  }
    73  
    74  // Stop stops the resource with the given id and unregisters it.
    75  // It returns any error from the underlying Stop call.
    76  // It does not return an error if the resource has already
    77  // been unregistered.
    78  func (rs *Resources) Stop(id string) error {
    79  	// We don't hold the mutex while calling Stop, because
    80  	// that might take a while and we don't want to
    81  	// stop all other resource manipulation while we do so.
    82  	// If resources.Stop is called concurrently, we'll get
    83  	// two concurrent calls to Stop, but that should fit
    84  	// well with the way we invariably implement Stop.
    85  	r := rs.Get(id)
    86  	if r == nil {
    87  		return nil
    88  	}
    89  	err := r.Stop()
    90  	rs.mu.Lock()
    91  	defer rs.mu.Unlock()
    92  	delete(rs.resources, id)
    93  	return err
    94  }
    95  
    96  // StopAll stops all the resources.
    97  func (rs *Resources) StopAll() {
    98  	rs.mu.Lock()
    99  	defer rs.mu.Unlock()
   100  	for _, r := range rs.resources {
   101  		if err := r.Stop(); err != nil {
   102  			logger.Errorf("error stopping %T resource: %v", r, err)
   103  		}
   104  	}
   105  	rs.resources = make(map[string]Resource)
   106  }
   107  
   108  // Count returns the number of resources currently held.
   109  func (rs *Resources) Count() int {
   110  	rs.mu.Lock()
   111  	defer rs.mu.Unlock()
   112  	return len(rs.resources)
   113  }
   114  
   115  // StringResource is just a regular 'string' that matches the Resource
   116  // interface.
   117  type StringResource string
   118  
   119  func (StringResource) Stop() error {
   120  	return nil
   121  }
   122  
   123  func (s StringResource) String() string {
   124  	return string(s)
   125  }