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 }