initial version of support project with build support.
[utils] / src / 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 /**
28  * Implements subscription and notification logic for an observer pattern.
29  * This class is thread safe. 
30  */
31 public class Observable<ObservableType, Event> {
32     
33     private static final Logger LOGGER = Logger.getLogger(Observable.class);
34     
35     /**
36      * Observable. 
37      */
38     private ObservableType _observable;
39     
40     /**
41      * Used to notify observers. 
42      */
43     private ObserverNotifier<ObservableType,Event> _notifier;
44     
45     /**
46      * Map of subscription to observer. 
47      */
48     private Map<Long, Observer<ObservableType,Event>> _observers;
49     
50     /**
51      * Counter for subscriptions. Holds the next subscription. 
52      */
53     private long _counter; 
54     
55     /**
56      * Constructs the observable. 
57      * @param aObservable Observable this instance is used for. 
58      * @param aNotifier Object used for implementing notification of listeners. 
59      */
60     public Observable(ObservableType aObservable, ObserverNotifier<ObservableType,Event> aNotifier) { 
61         _observable = aObservable;
62         _notifier = aNotifier; 
63         _observers = new TreeMap<Long, Observer<ObservableType, Event>>();
64         _counter = 0; 
65     }
66     
67     /**
68      * Subscribe an obvers. 
69      * @param aObserver Observer to subscribe. 
70      * @return Event Event to send. 
71      */
72     public synchronized long subscribe(Observer<ObservableType, Event> aObserver) {
73         long subscription = _counter++; // integer rage is so large it will never roll over. 
74         _observers.put(subscription, aObserver);
75         return subscription; 
76     }
77     
78     /**
79      * Unsubscribe an observer. 
80      * @param aSubscription Subscription which is used
81      * @throws IllegalArgumentException In case the subscription is not known. 
82      */
83     public synchronized void unsubscribe(long aSubscription) {
84         Object obj = _observers.remove(aSubscription);
85         if ( obj == null ) { 
86             throw new IllegalArgumentException("Subscription '" + aSubscription + "'");
87         }
88     }
89     
90     /**
91      * Gets the number of subscribed observers. 
92      */
93     public int getObserverCount() { 
94         return _observers.size(); 
95     }
96     
97     /**
98      * Notifies all subscribed observers. 
99      * @param aEvent Event to send. 
100      */
101     public void send(Event aEvent) {
102         // Make sure we do the notification while not holding the lock to avoid potential deadlock
103         // situations. 
104         List<Observer<ObservableType,Event>> observers = new ArrayList<Observer<ObservableType, Event>>();
105         synchronized (this) { 
106             observers.addAll(_observers.values());
107         }
108         for (Observer<ObservableType,Event> observer: observers) { 
109             _notifier.update(observer, _observable, aEvent); 
110         }
111     }
112     
113     /* (non-Javadoc)
114      * @see java.lang.Object#finalize()
115      */
116     @Override
117     protected void finalize() throws Throwable {
118         if ( _observers.size() > 0 ) { 
119             LOGGER.error("Still observers registered at finalization of observer!"); 
120             for (Observer observer: _observers.values()) { 
121                 LOGGER.error("  observer: " + observer);
122             }
123         }
124         
125         super.finalize();
126     }
127
128 }