(no commit message)
[utils] / support / general / src / main / java / org / wamblee / observer / Observable.java
1 /*
2  * Copyright 2005-2010 the original author or authors.
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  * 
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  * 
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */ 
16 package org.wamblee.observer;
17
18 import org.apache.log4j.Logger;
19
20 import java.util.ArrayList;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.TreeMap;
24
25 /**
26  * Implements subscription and notification logic for an observer pattern. This
27  * class is thread safe.
28  */
29 public class Observable<ObservableType, Event> {
30     private static final Logger LOGGER = Logger.getLogger(Observable.class);
31
32     /**
33      * Observable.
34      */
35     private ObservableType observable;
36
37     /**
38      * Used to notify observers.
39      */
40     private ObserverNotifier<ObservableType, Event> notifier;
41
42     /**
43      * Map of subscription to observer.
44      */
45     private Map<Long, Observer<ObservableType, Event>> observers;
46
47     /**
48      * Counter for subscriptions. Holds the next subscription.
49      */
50     private long counter;
51
52     /**
53      * Constructs the observable.
54      * 
55      * @param aObservable
56      *            Observable this instance is used for.
57      * @param aNotifier
58      *            Object used for implementing notification of listeners.
59      */
60     public Observable(ObservableType aObservable,
61         ObserverNotifier<ObservableType, Event> aNotifier) {
62         observable = aObservable;
63         notifier = aNotifier;
64         observers = new TreeMap<Long, Observer<ObservableType, Event>>();
65         counter = 0;
66     }
67
68     /**
69      * Subscribe an obvers.
70      * 
71      * @param aObserver
72      *            Observer to subscribe.
73      * @return Event Event to send.
74      */
75     public synchronized long subscribe(Observer<ObservableType, Event> aObserver) {
76         long subscription = counter++; // integer rage is so large it will
77         // never roll over.
78
79         observers.put(subscription, aObserver);
80
81         return subscription;
82     }
83
84     /**
85      * Unsubscribe an observer.
86      * 
87      * @param aSubscription
88      *            Subscription which is used
89      * @throws IllegalArgumentException
90      *             In case the subscription is not known.
91      */
92     public synchronized void unsubscribe(long aSubscription) {
93         Object obj = observers.remove(aSubscription);
94
95         if (obj == null) {
96             throw new IllegalArgumentException("Subscription '" +
97                 aSubscription + "'");
98         }
99     }
100
101     /**
102      * Gets the number of subscribed observers.
103      * 
104      * @return Number of subscribed observers.
105      */
106     public int getObserverCount() {
107         return observers.size();
108     }
109
110     /**
111      * Notifies all subscribed observers.
112      * 
113      * @param aEvent
114      *            Event to send.
115      */
116     public void send(Event aEvent) {
117         // Make sure we do the notification while not holding the lock to avoid
118         // potential deadlock
119         // situations.
120         List<Observer<ObservableType, Event>> myObservers = new ArrayList<Observer<ObservableType, Event>>();
121
122         synchronized (this) {
123             myObservers.addAll(observers.values());
124         }
125
126         for (Observer<ObservableType, Event> observer : myObservers) {
127             notifier.update(observer, observable, aEvent);
128         }
129     }
130 }