github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/worker/notifyworker.go (about)

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