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.concurrent.CopyOnWriteArraySet; 028 import net.jcip.annotations.ThreadSafe; 029 import org.jboss.dna.common.util.Logger; 030 031 /** 032 * Abstract class that is used to signal that a change set has occurred. This class is typically subclassed by those that wish to 033 * observe changes in content, and {@link Observable#register(ChangeObserver) registered} with a {@link Observable}. 034 * <p> 035 * This class maintains a (weak) reference to the ChangeSource instances with which it is registered. Therefore, the observers 036 * will not keep a ChangeSource from being garbage collected. And, if a change source is garbage collected, calling 037 * {@link #unregister()} will clean up naturally. 038 * </p> 039 */ 040 @ThreadSafe 041 public abstract class ChangeObserver implements Observer { 042 043 private final CopyOnWriteArraySet<ChangeSourceReference> sources = new CopyOnWriteArraySet<ChangeSourceReference>(); 044 045 protected ChangeObserver() { 046 } 047 048 /** 049 * Records that this listener has successfully registered by the supplied {@link Observable}. 050 * 051 * @param source the source with which this listener was registered 052 */ 053 final void registeredWith( Observable source ) { 054 sources.add(new ChangeSourceReference(source)); 055 } 056 057 /** 058 * Records that this listener has successfully unregistered by the supplied {@link Observable}. 059 * 060 * @param source the source with which this listener was registered 061 */ 062 final void unregisteredWith( Observable source ) { 063 sources.remove(new ChangeSourceReference(source)); 064 } 065 066 /** 067 * Unregister this listener from all {@link Observable sources} that it was registered with. This is preferred over calling 068 * {@link Observable#unregister(ChangeObserver)} directly. 069 */ 070 public void unregister() { 071 doUnregister(); 072 } 073 074 /** 075 * Method called by {@link #unregister()} that actually does the unregistering. This method is final. 076 */ 077 protected final void doUnregister() { 078 // Unregister this listener from each source ... 079 for (ChangeSourceReference sourceReference : sources) { 080 Observable source = sourceReference.get(); 081 if (source != null) { 082 try { 083 source.unregister(this); 084 } catch (Throwable t) { 085 Logger.getLogger(getClass()).debug(t, "Error while unregistering {0} from {1}", this, source); 086 } 087 } 088 } 089 } 090 091 /** 092 * Determine whether this observer is currently registered with any {@link Observable} instances. 093 * <p> 094 * Although an observer might be registered with an {@link Observable}, if that Observable is garbage collected, then this 095 * observer will no longer be registered with it. 096 * </p> 097 * 098 * @return true if this observer is registered with at least one {@link Observable} instance, or false if this observer is not 099 * currently registered with any {@link Observable} instances. 100 */ 101 public boolean isRegistered() { 102 for (ChangeSourceReference reference : sources) { 103 if (reference.get() != null) return true; 104 } 105 return false; 106 } 107 108 /** 109 * Method that is called for each {@link Changes} from the {@link Observable} instance(s) with which this listener is 110 * registered. 111 * 112 * @param changeSet the change set 113 */ 114 public abstract void notify( Changes changeSet ); 115 116 /** 117 * A {@link WeakReference} implementation that provides a valid 118 */ 119 protected final class ChangeSourceReference extends WeakReference<Observable> { 120 final int hc; 121 122 protected ChangeSourceReference( Observable source ) { 123 super(source); 124 this.hc = source.hashCode(); 125 } 126 127 /** 128 * {@inheritDoc} 129 * 130 * @see java.lang.Object#hashCode() 131 */ 132 @Override 133 public int hashCode() { 134 return hc; 135 } 136 137 /** 138 * {@inheritDoc} 139 * 140 * @see java.lang.Object#equals(java.lang.Object) 141 */ 142 @Override 143 public boolean equals( Object obj ) { 144 if (obj == this) return true; 145 if (obj instanceof ChangeSourceReference) { 146 ChangeSourceReference that = (ChangeSourceReference)obj; 147 Observable thisSource = this.get(); 148 Observable thatSource = that.get(); 149 return thisSource == thatSource; // reference equality, not object equality! 150 } 151 if (obj instanceof Observable) { 152 Observable that = (Observable)obj; 153 return this.get() == that; // reference equality, not object equality! 154 } 155 return false; 156 } 157 } 158 }