github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/watcher/notify.go (about)

     1  // Copyright 2012-2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package watcher
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  
     9  	"github.com/juju/juju/worker/catacomb"
    10  )
    11  
    12  // NotifyChannel is a change channel as described in the CoreWatcher docs.
    13  //
    14  // It sends a single value to indicate that the watch is active, and subsequent
    15  // values whenever the value(s) under observation change(s).
    16  type NotifyChannel <-chan struct{}
    17  
    18  // NotifyWatcher conveniently ties a NotifyChannel to the worker.Worker that
    19  // represents its validity.
    20  type NotifyWatcher interface {
    21  	CoreWatcher
    22  	Changes() NotifyChannel
    23  }
    24  
    25  // NotifyHandler defines the operation of a NotifyWorker.
    26  type NotifyHandler interface {
    27  
    28  	// SetUp is called once when creating a NotifyWorker. It must return a
    29  	// NotifyWatcher or an error. The NotifyHandler takes responsibility for
    30  	// stopping any returned watcher and handling any errors.
    31  	SetUp() (NotifyWatcher, error)
    32  
    33  	// Handle is called whenever a value is received from the NotifyWatcher
    34  	// returned by SetUp. If it returns an error, the NotifyWorker will be
    35  	// stopped.
    36  	//
    37  	// If Handle runs any blocking operations it must pass through, or select
    38  	// on, the supplied abort channel; this channnel will be closed when the
    39  	// NotifyWorker is killed. An aborted Handle should not return an error.
    40  	Handle(abort <-chan struct{}) error
    41  
    42  	// TearDown is called once when stopping a NotifyWorker, whether or not
    43  	// SetUp succeeded. It need not concern itself with the NotifyWatcher, but
    44  	// must clean up any other resources created in SetUp or Handle.
    45  	TearDown() error
    46  }
    47  
    48  // NotifyConfig holds the direct dependencies of a NotifyWorker.
    49  type NotifyConfig struct {
    50  	Handler NotifyHandler
    51  }
    52  
    53  // Validate returns an error if the config cannot start a NotifyWorker.
    54  func (config NotifyConfig) Validate() error {
    55  	if config.Handler == nil {
    56  		return errors.NotValidf("nil Handler")
    57  	}
    58  	return nil
    59  }
    60  
    61  // NewNotifyWorker starts a new worker that runs a NotifyHandler.
    62  func NewNotifyWorker(config NotifyConfig) (*NotifyWorker, error) {
    63  	if err := config.Validate(); err != nil {
    64  		return nil, errors.Trace(err)
    65  	}
    66  	nw := &NotifyWorker{
    67  		config: config,
    68  	}
    69  	err := catacomb.Invoke(catacomb.Plan{
    70  		Site: &nw.catacomb,
    71  		Work: nw.loop,
    72  	})
    73  	if err != nil {
    74  		return nil, errors.Trace(err)
    75  	}
    76  	return nw, nil
    77  }
    78  
    79  // NotifyWorker is a worker that wraps a NotifyWatcher.
    80  type NotifyWorker struct {
    81  	config   NotifyConfig
    82  	catacomb catacomb.Catacomb
    83  }
    84  
    85  func (nw *NotifyWorker) loop() (err error) {
    86  	changes := nw.setUp()
    87  	defer nw.tearDown(err)
    88  	abort := nw.catacomb.Dying()
    89  	for {
    90  		select {
    91  		case <-abort:
    92  			return nw.catacomb.ErrDying()
    93  		case _, ok := <-changes:
    94  			if !ok {
    95  				return errors.New("change channel closed")
    96  			}
    97  			err = nw.config.Handler.Handle(abort)
    98  			if err != nil {
    99  				return err
   100  			}
   101  		}
   102  	}
   103  }
   104  
   105  // setUp calls the handler's SetUp method; registers any returned watcher with
   106  // the worker's catacomb; and returns the watcher's changes channel. Any errors
   107  // encountered kill the worker and cause a nil channel to be returned.
   108  func (nw *NotifyWorker) setUp() NotifyChannel {
   109  	watcher, err := nw.config.Handler.SetUp()
   110  	if err != nil {
   111  		nw.catacomb.Kill(err)
   112  	}
   113  	if watcher == nil {
   114  		nw.catacomb.Kill(errors.New("handler returned nil watcher"))
   115  	} else if err := nw.catacomb.Add(watcher); err != nil {
   116  		nw.catacomb.Kill(err)
   117  	} else {
   118  		return watcher.Changes()
   119  	}
   120  	return nil
   121  }
   122  
   123  // tearDown kills the worker with the supplied error; and then kills it with
   124  // any error returned by the handler's TearDown method.
   125  func (nw *NotifyWorker) tearDown(err error) {
   126  	nw.catacomb.Kill(err)
   127  	err = nw.config.Handler.TearDown()
   128  	nw.catacomb.Kill(err)
   129  }
   130  
   131  // Kill is part of the worker.Worker interface.
   132  func (nw *NotifyWorker) Kill() {
   133  	nw.catacomb.Kill(nil)
   134  }
   135  
   136  // Wait is part of the worker.Worker interface.
   137  func (nw *NotifyWorker) Wait() error {
   138  	return nw.catacomb.Wait()
   139  }