/*- * Copyright © 2009 Diamond Light Source Ltd., Science and Technology * Facilities Council Daresbury Laboratory * * This file is part of GDA. * * GDA is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License version 3 as published by the Free * Software Foundation. * * GDA is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with GDA. If not, see <http://www.gnu.org/licenses/>. */ package gda.observable; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A Component that may be used by Observable objects to maintain its list of IObservers DO NOT synchronize the methods of this class as doing so in the past * led to deadlocks when used with DOFS and AbosulteMove objects */ public class ObservableComponent implements IObservable, IIsBeingObserved { private final Set<IObserver> myIObservers = new CopyOnWriteArraySet<>(); private static final Logger logger = LoggerFactory.getLogger(ObservableComponent.class); /** * Add an observer to the list of observers providing that the list does not already contain an instance of the observer on it. {@inheritDoc} * * @param anIObserver * the observer * @see gda.observable.IObservable#addIObserver(gda.observable.IObserver) */ @Override public void addIObserver(IObserver anIObserver) { if (anIObserver == null) { return; } myIObservers.add(anIObserver); } /** * Delete the instance of this observer from the list observers. {@inheritDoc} * * @param anIObserver * the observer * @see gda.observable.IObservable#deleteIObserver(gda.observable.IObserver) */ @Override public void deleteIObserver(IObserver anIObserver) { if (anIObserver == null) { return; } myIObservers.remove(anIObserver); } /** * Delete all observers from the list of observers. {@inheritDoc} * * @see gda.observable.IObservable#deleteIObservers() */ @Override public void deleteIObservers() { myIObservers.clear(); } /** * Notify all observers on the list of the requested change, swallowing any exceptions to ensure all observers are updated. * * @param theObserved * the observed component * @param changeCode * the data to be sent to the observer. */ public void notifyIObservers(Object theObserved, Object changeCode) { for (IObserver anIObserver : myIObservers) { try { sendEventToObserver(anIObserver, theObserved, changeCode); } catch (Exception ex) { // Don't log the full stack trace to keep the log tidier, full trace is in debug if enabled logger.error("swallowing exception: {}", ex.toString()); /* * Try to log something useful about the args of update which caused the exception to be thrown, but beware: ServerSideEventDispatcher.update * details how calling toString (including via {} in logger) on Scannables can cause deadlocks...so just use class names to describe problem. */ final String anIObserverDescription = anIObserver == null ? "null" : anIObserver.getClass().getName(); final String theObservedDescription = theObserved == null ? "null" : theObserved.getClass().getName(); if (logger.isDebugEnabled()) { logger.debug("triggered by {}.update({}, {})", anIObserverDescription, theObservedDescription, changeCode, ex); } } } } protected void sendEventToObserver(IObserver anIObserver, Object theObserved, Object changeCode) { anIObserver.update(theObserved, changeCode); } @Override public boolean IsBeingObserved() { return !myIObservers.isEmpty(); } }