820db38c8bb3241b61d3d3a12ab19585351ae4d4
[utils] / support / general / src / main / java / org / wamblee / observer / Observable.java
1 /*
2  * Copyright 2005 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
17 package org.wamblee.observer;
18
19 import java.util.List;
20 import java.util.ArrayList;
21 import java.util.Map;
22 import java.util.TreeMap;
23
24 import org.apache.log4j.Logger;
25
26 /**
27  * Implements subscription and notification logic for an observer pattern. This
28  * class is thread safe.
29  */
30 public class Observable<ObservableType, Event> {
31
32     private static final Logger LOGGER = Logger.getLogger(Observable.class);
33
34     /**
35      * Observable.
36      */
37     private ObservableType observable;
38
39     /**
40      * Used to notify observers.
41      */
42     private ObserverNotifier<ObservableType, Event> notifier;
43
44     /**
45      * Map of subscription to observer.
46      */
47     private Map<Long, Observer<ObservableType, Event>> observers;
48
49     /**
50      * Counter for subscriptions. Holds the next subscription.
51      */
52     private long counter;
53
54     /**
55      * Constructs the observable.
56      * 
57      * @param aObservable
58      *            Observable this instance is used for.
59      * @param aNotifier
60      *            Object used for implementing notification of listeners.
61      */
62     public Observable(ObservableType aObservable,
63             ObserverNotifier<ObservableType, Event> aNotifier) {
64         observable = aObservable;
65         notifier = aNotifier;
66         observers = new TreeMap<Long, Observer<ObservableType, Event>>();
67         counter = 0;
68     }
69
70     /**
71      * Subscribe an obvers.
72      * 
73      * @param aObserver
74      *            Observer to subscribe.
75      * @return Event Event to send.
76      */
77     public synchronized long subscribe(Observer<ObservableType, Event> aObserver) {
78         long subscription = counter++; // integer rage is so large it will
79                                         // never roll over.
80         observers.put(subscription, aObserver);
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         if (obj == null) {
95             throw new IllegalArgumentException("Subscription '" + aSubscription
96                     + "'");
97         }
98     }
99
100     /**
101      * Gets the number of subscribed observers.
102      * 
103      * @return Number of subscribed observers.
104      */
105     public int getObserverCount() {
106         return observers.size();
107     }
108
109     /**
110      * Notifies all subscribed observers.
111      * 
112      * @param aEvent
113      *            Event to send.
114      */
115     public void send(Event aEvent) {
116         // Make sure we do the notification while not holding the lock to avoid
117         // potential deadlock
118         // situations.
119         List<Observer<ObservableType, Event>> myObservers = new ArrayList<Observer<ObservableType, Event>>();
120         synchronized (this) {
121             myObservers.addAll(observers.values());
122         }
123         for (Observer<ObservableType, Event> observer : myObservers) {
124             notifier.update(observer, observable, aEvent);
125         }
126     }
127
128     /*
129      * (non-Javadoc)
130      * 
131      * @see java.lang.Object#finalize()
132      */
133     @Override
134     protected void finalize() throws Throwable {
135         if (observers.size() > 0) {
136             LOGGER
137                     .error("Still observers registered at finalization of observer!");
138             for (Observer observer : observers.values()) {
139                 LOGGER.error("  observer: " + observer);
140             }
141         }
142
143         super.finalize();
144     }
145
146 }