forked from juju/juju
/
observer.go
117 lines (98 loc) · 3.2 KB
/
observer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package observer
import (
"net/http"
"sync"
"github.com/juju/juju/rpc"
"gopkg.in/juju/names.v2"
)
// Observer defines a type which will observe API server events as
// they happen.
type Observer interface {
rpc.ObserverFactory
// Login informs an Observer that an entity has logged in.
Login(entity names.Tag, model names.ModelTag, fromController bool, userData string)
// Join is called when the connection to the API server's
// WebSocket is opened.
Join(req *http.Request, connectionID uint64)
// Leave is called when the connection to the API server's
// WebSocket is closed.
Leave()
}
// ObserverFactory is a function which creates an Observer.
type ObserverFactory func() Observer
// ObserverFactoryMultiplexer returns an ObserverFactory which will
// return a Multiplexer of all the observers instantiated from the
// factories passed in.
func ObserverFactoryMultiplexer(factories ...ObserverFactory) ObserverFactory {
return func() Observer {
observers := make([]Observer, 0, len(factories))
for _, newObserver := range factories {
observers = append(observers, newObserver())
}
return NewMultiplexer(observers...)
}
}
// None is a wrapper around the Multiplexer factory to add clarity to
// code that doesn't need any observers.
func None() *Multiplexer {
return NewMultiplexer()
}
// NewMultiplexer creates a new Multiplexer with the provided
// observers.
func NewMultiplexer(observers ...Observer) *Multiplexer {
return &Multiplexer{
observers: removeNilObservers(observers),
}
}
func removeNilObservers(observers []Observer) []Observer {
var validatedObservers []Observer
for _, o := range observers {
if o == nil {
continue
}
validatedObservers = append(validatedObservers, o)
}
return validatedObservers
}
// Multiplexer multiplexes calls to an arbitray number of observers.
type Multiplexer struct {
observers []Observer
}
// Join is called when the connection to the API server's WebSocket is
// opened.
func (m *Multiplexer) Join(req *http.Request, connectionID uint64) {
mapConcurrent(func(o Observer) { o.Join(req, connectionID) }, m.observers)
}
// Leave implements Observer.
func (m *Multiplexer) Leave() {
mapConcurrent(Observer.Leave, m.observers)
}
// Login implements Observer.
func (m *Multiplexer) Login(entity names.Tag, model names.ModelTag, fromController bool, userData string) {
mapConcurrent(func(o Observer) { o.Login(entity, model, fromController, userData) }, m.observers)
}
// RPCObserver implements Observer. It will create an
// rpc.ObserverMultiplexer by calling all the Observer's RPCObserver
// methods.
func (m *Multiplexer) RPCObserver() rpc.Observer {
rpcObservers := make([]rpc.Observer, len(m.observers))
for i, o := range m.observers {
rpcObservers[i] = o.RPCObserver()
}
return rpc.NewObserverMultiplexer(rpcObservers...)
}
// mapConcurrent calls fn on all observers concurrently and then waits
// for all calls to exit before returning.
func mapConcurrent(fn func(Observer), observers []Observer) {
var wg sync.WaitGroup
wg.Add(len(observers))
defer wg.Wait()
for _, o := range observers {
go func(obs Observer) {
defer wg.Done()
fn(obs)
}(o)
}
}