initial version of support project with build support.
[utils] / src / org / wamblee / observer / Observable.java
diff --git a/src/org/wamblee/observer/Observable.java b/src/org/wamblee/observer/Observable.java
new file mode 100644 (file)
index 0000000..7af7ea7
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2005 the original author or authors.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+package org.wamblee.observer;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.log4j.Logger;
+
+
+/**
+ * Implements subscription and notification logic for an observer pattern.
+ * This class is thread safe. 
+ */
+public class Observable<ObservableType, Event> {
+    
+    private static final Logger LOGGER = Logger.getLogger(Observable.class);
+    
+    /**
+     * Observable. 
+     */
+    private ObservableType _observable;
+    
+    /**
+     * Used to notify observers. 
+     */
+    private ObserverNotifier<ObservableType,Event> _notifier;
+    
+    /**
+     * Map of subscription to observer. 
+     */
+    private Map<Long, Observer<ObservableType,Event>> _observers;
+    
+    /**
+     * Counter for subscriptions. Holds the next subscription. 
+     */
+    private long _counter; 
+    
+    /**
+     * Constructs the observable. 
+     * @param aObservable Observable this instance is used for. 
+     * @param aNotifier Object used for implementing notification of listeners. 
+     */
+    public Observable(ObservableType aObservable, ObserverNotifier<ObservableType,Event> aNotifier) { 
+        _observable = aObservable;
+        _notifier = aNotifier; 
+        _observers = new TreeMap<Long, Observer<ObservableType, Event>>();
+        _counter = 0; 
+    }
+    
+    /**
+     * Subscribe an obvers. 
+     * @param aObserver Observer to subscribe. 
+     * @return Event Event to send. 
+     */
+    public synchronized long subscribe(Observer<ObservableType, Event> aObserver) {
+        long subscription = _counter++; // integer rage is so large it will never roll over. 
+        _observers.put(subscription, aObserver);
+        return subscription; 
+    }
+    
+    /**
+     * Unsubscribe an observer. 
+     * @param aSubscription Subscription which is used
+     * @throws IllegalArgumentException In case the subscription is not known. 
+     */
+    public synchronized void unsubscribe(long aSubscription) {
+        Object obj = _observers.remove(aSubscription);
+        if ( obj == null ) { 
+            throw new IllegalArgumentException("Subscription '" + aSubscription + "'");
+        }
+    }
+    
+    /**
+     * Gets the number of subscribed observers. 
+     */
+    public int getObserverCount() { 
+        return _observers.size(); 
+    }
+    
+    /**
+     * Notifies all subscribed observers. 
+     * @param aEvent Event to send. 
+     */
+    public void send(Event aEvent) {
+        // Make sure we do the notification while not holding the lock to avoid potential deadlock
+        // situations. 
+        List<Observer<ObservableType,Event>> observers = new ArrayList<Observer<ObservableType, Event>>();
+        synchronized (this) { 
+            observers.addAll(_observers.values());
+        }
+        for (Observer<ObservableType,Event> observer: observers) { 
+            _notifier.update(observer, _observable, aEvent); 
+        }
+    }
+    
+    /* (non-Javadoc)
+     * @see java.lang.Object#finalize()
+     */
+    @Override
+    protected void finalize() throws Throwable {
+        if ( _observers.size() > 0 ) { 
+            LOGGER.error("Still observers registered at finalization of observer!"); 
+            for (Observer observer: _observers.values()) { 
+                LOGGER.error("  observer: " + observer);
+            }
+        }
+        
+        super.finalize();
+    }
+
+}