001 /* 002 * JBoss DNA (http://www.jboss.org/dna) 003 * See the COPYRIGHT.txt file distributed with this work for information 004 * regarding copyright ownership. Some portions may be licensed 005 * to Red Hat, Inc. under one or more contributor license agreements. 006 * See the AUTHORS.txt file in the distribution for a full listing of 007 * individual contributors. 008 * 009 * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA 010 * is licensed to you under the terms of the GNU Lesser General Public License as 011 * published by the Free Software Foundation; either version 2.1 of 012 * the License, or (at your option) any later version. 013 * 014 * JBoss DNA is distributed in the hope that it will be useful, 015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 017 * Lesser General Public License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this software; if not, write to the Free 021 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 022 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 023 */ 024 package org.jboss.dna.graph.observe; 025 026 import java.lang.ref.WeakReference; 027 import java.util.Iterator; 028 import java.util.concurrent.CopyOnWriteArrayList; 029 import java.util.concurrent.atomic.AtomicBoolean; 030 import net.jcip.annotations.ThreadSafe; 031 import org.jboss.dna.common.util.CheckArg; 032 import org.jboss.dna.common.util.Logger; 033 034 /** 035 * Reusable manager of change listeners, typically employed by another {@link Observable} implementation. 036 */ 037 @ThreadSafe 038 public class ChangeObservers implements Observable { 039 040 private final CopyOnWriteArrayList<ObserverReference> observers = new CopyOnWriteArrayList<ObserverReference>(); 041 private final AtomicBoolean shutdown = new AtomicBoolean(false); 042 043 public ChangeObservers() { 044 } 045 046 /** 047 * {@inheritDoc} 048 * 049 * @see org.jboss.dna.graph.observe.Observable#register(org.jboss.dna.graph.observe.ChangeObserver) 050 */ 051 public boolean register( ChangeObserver observer ) { 052 if (observer != null && !shutdown.get() && observers.addIfAbsent(new ObserverReference(observer))) { 053 observer.registeredWith(this); 054 return true; 055 } 056 return false; 057 } 058 059 /** 060 * {@inheritDoc} 061 * 062 * @see org.jboss.dna.graph.observe.Observable#unregister(org.jboss.dna.graph.observe.ChangeObserver) 063 */ 064 public boolean unregister( ChangeObserver observer ) { 065 if (observer != null && observers.remove(observer)) { 066 observer.unregisteredWith(this); 067 return true; 068 } 069 return false; 070 } 071 072 /** 073 * Unregister all registered observers, and mark this as no longer accepting new registered observers. 074 */ 075 public void shutdown() { 076 shutdown.set(true); 077 while (!observers.isEmpty()) { 078 Iterator<ObserverReference> iter = observers.iterator(); // gets snapshot 079 observers.clear(); 080 while (iter.hasNext()) { 081 ObserverReference reference = iter.next(); 082 if (reference.get() != null) reference.get().unregisteredWith(this); 083 } 084 } 085 } 086 087 /** 088 * Determine whether there are any observers at the time this method is called. 089 * 090 * @return true if there are currently no observers, or false if there is at least one observer 091 */ 092 public boolean isEmpty() { 093 return observers.isEmpty(); 094 } 095 096 /** 097 * Broadcast the supplied changes to the registered observers. 098 * 099 * @param changes the changes to broadcast 100 * @throws IllegalArgumentException if the changes reference is null 101 */ 102 public void broadcast( Changes changes ) { 103 CheckArg.isNotNull(changes, "changes"); 104 for (ObserverReference observerReference : observers) { 105 ChangeObserver observer = observerReference.get(); 106 if (observer == null) { 107 observers.remove(observerReference); 108 continue; 109 } 110 try { 111 observer.notify(changes); 112 } catch (Throwable t) { 113 Logger.getLogger(getClass()).debug(t, "Exception while notifying"); 114 } 115 } 116 } 117 118 /** 119 * A {@link WeakReference} implementation that provides a valid 120 */ 121 protected final class ObserverReference extends WeakReference<ChangeObserver> { 122 final int hc; 123 124 protected ObserverReference( ChangeObserver source ) { 125 super(source); 126 this.hc = source.hashCode(); 127 } 128 129 /** 130 * {@inheritDoc} 131 * 132 * @see java.lang.Object#hashCode() 133 */ 134 @Override 135 public int hashCode() { 136 return hc; 137 } 138 139 /** 140 * {@inheritDoc} 141 * 142 * @see java.lang.Object#equals(java.lang.Object) 143 */ 144 @Override 145 public boolean equals( Object obj ) { 146 if (obj == this) return true; 147 if (obj instanceof ObserverReference) { 148 ObserverReference that = (ObserverReference)obj; 149 ChangeObserver thisSource = this.get(); 150 ChangeObserver thatSource = that.get(); 151 return thisSource == thatSource; // reference equality, not object equality! 152 } 153 if (obj instanceof ChangeObserver) { 154 ChangeObserver that = (ChangeObserver)obj; 155 return this.get() == that; // reference equality, not object equality! 156 } 157 return false; 158 } 159 } 160 }