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

     1  // Copyright 2012-2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package legacy
     5  
     6  import (
     7  	"launchpad.net/tomb"
     8  
     9  	"github.com/juju/juju/state"
    10  	"github.com/juju/juju/state/watcher"
    11  	"github.com/juju/juju/worker"
    12  )
    13  
    14  // ensureErr is defined as a variable to allow the test suite
    15  // to override it.
    16  var ensureErr = watcher.EnsureErr
    17  
    18  // notifyWorker is the internal implementation of the Worker
    19  // interface, using a NotifyWatcher for handling changes.
    20  type notifyWorker struct {
    21  	tomb    tomb.Tomb
    22  	handler NotifyWatchHandler
    23  }
    24  
    25  // NotifyWatchHandler implements the business logic that is triggered
    26  // as part of watching a NotifyWatcher.
    27  type NotifyWatchHandler interface {
    28  	// SetUp will be called once, and should return the watcher that will
    29  	// be used to trigger subsequent Handle()s. SetUp can return a watcher
    30  	// even if there is an error, and the notify worker will make sure
    31  	// to stop the watcher.
    32  	SetUp() (state.NotifyWatcher, error)
    33  
    34  	// TearDown should cleanup any resources that are left around.
    35  	TearDown() error
    36  
    37  	// Handle is called whenever the watcher returned from SetUp sends a value
    38  	// on its Changes() channel. The done channel will be closed if and when
    39  	// the worker is being interrupted to finish. Any worker should avoid any
    40  	// bare channel reads or writes, but instead use a select with the done
    41  	// channel.
    42  	Handle(done <-chan struct{}) error
    43  }
    44  
    45  // NewNotifyWorker starts a new worker running the business logic from
    46  // the handler. The worker loop is started in another goroutine as a
    47  // side effect of calling this.
    48  func NewNotifyWorker(handler NotifyWatchHandler) worker.Worker {
    49  	nw := &notifyWorker{
    50  		handler: handler,
    51  	}
    52  
    53  	go func() {
    54  		defer nw.tomb.Done()
    55  		nw.tomb.Kill(nw.loop())
    56  	}()
    57  	return nw
    58  }
    59  
    60  // Kill is part of the worker.Worker interface.
    61  func (nw *notifyWorker) Kill() {
    62  	nw.tomb.Kill(nil)
    63  }
    64  
    65  // Wait is part of the worker.Worker interface.
    66  func (nw *notifyWorker) Wait() error {
    67  	return nw.tomb.Wait()
    68  }
    69  
    70  type tearDowner interface {
    71  	TearDown() error
    72  }
    73  
    74  // propagateTearDown tears down the handler, but ensures any error is
    75  // propagated through the tomb's Kill method.
    76  func propagateTearDown(handler tearDowner, t *tomb.Tomb) {
    77  	if err := handler.TearDown(); err != nil {
    78  		t.Kill(err)
    79  	}
    80  }
    81  
    82  func (nw *notifyWorker) loop() error {
    83  	w, err := nw.handler.SetUp()
    84  	if err != nil {
    85  		if w != nil {
    86  			// We don't bother to propagate an error, because we
    87  			// already have an error
    88  			w.Stop()
    89  		}
    90  		return err
    91  	}
    92  	defer propagateTearDown(nw.handler, &nw.tomb)
    93  	defer watcher.Stop(w, &nw.tomb)
    94  	for {
    95  		select {
    96  		case <-nw.tomb.Dying():
    97  			return tomb.ErrDying
    98  		case _, ok := <-w.Changes():
    99  			if !ok {
   100  				return ensureErr(w)
   101  			}
   102  			if err := nw.handler.Handle(nw.tomb.Dying()); err != nil {
   103  				return err
   104  			}
   105  		}
   106  	}
   107  }