diff options
author | Manuel Roman <manuelroman@google.com> | 2011-09-13 15:57:27 -0700 |
---|---|---|
committer | Manuel Roman <manuelroman@google.com> | 2011-09-13 16:49:34 -0700 |
commit | 3742d9db8b6edb10627b0f89336cca5249f1d15a (patch) | |
tree | 32a130b2a53160c4196bb23ec9ec7f25387f717d /src/javax/jmdns/impl/ListenerStatus.java | |
parent | 19376825c9e562c188aef9ccd09a7220bd3c0a20 (diff) | |
download | jmdns-3742d9db8b6edb10627b0f89336cca5249f1d15a.tar.gz |
Uploading the original JmDNS code
This code is used by the Broker service to advertise
and discover an Active Home using the standard
mDNS protocol
Change-Id: I5d7068765b4ca771e3f3faf5d12104e2247945f3
Diffstat (limited to 'src/javax/jmdns/impl/ListenerStatus.java')
-rw-r--r-- | src/javax/jmdns/impl/ListenerStatus.java | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/src/javax/jmdns/impl/ListenerStatus.java b/src/javax/jmdns/impl/ListenerStatus.java new file mode 100644 index 0000000..a33842c --- /dev/null +++ b/src/javax/jmdns/impl/ListenerStatus.java @@ -0,0 +1,286 @@ +/** + * + */ +package javax.jmdns.impl; + +import java.util.EventListener; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.logging.Logger; + +import javax.jmdns.JmDNS; +import javax.jmdns.ServiceEvent; +import javax.jmdns.ServiceInfo; +import javax.jmdns.ServiceListener; +import javax.jmdns.ServiceTypeListener; + +/** + * This class track the status of listener.<br/> + * The main purpose of this class is to collapse consecutive events so that we can guarantee the correct call back sequence. + * + * @param <T> + * listener type + */ +public class ListenerStatus<T extends EventListener> { + + public static class ServiceListenerStatus extends ListenerStatus<ServiceListener> { + private static Logger logger = Logger.getLogger(ServiceListenerStatus.class.getName()); + + private final ConcurrentMap<String, ServiceInfo> _addedServices; + + /** + * @param listener + * listener being tracked. + * @param synch + * true if that listener can be called asynchronously + */ + public ServiceListenerStatus(ServiceListener listener, boolean synch) { + super(listener, synch); + _addedServices = new ConcurrentHashMap<String, ServiceInfo>(32); + } + + /** + * A service has been added.<br/> + * <b>Note:</b>This event is only the service added event. The service info associated with this event does not include resolution information.<br/> + * To get the full resolved information you need to listen to {@link #serviceResolved(ServiceEvent)} or call {@link JmDNS#getServiceInfo(String, String, long)} + * + * <pre> + * ServiceInfo info = event.getDNS().getServiceInfo(event.getType(), event.getName()) + * </pre> + * <p> + * Please note that service resolution may take a few second to resolve. + * </p> + * + * @param event + * The ServiceEvent providing the name and fully qualified type of the service. + */ + void serviceAdded(ServiceEvent event) { + String qualifiedName = event.getName() + "." + event.getType(); + if (null == _addedServices.putIfAbsent(qualifiedName, event.getInfo().clone())) { + this.getListener().serviceAdded(event); + ServiceInfo info = event.getInfo(); + if ((info != null) && (info.hasData())) { + this.getListener().serviceResolved(event); + } + } else { + logger.finer("Service Added called for a service already added: " + event); + } + } + + /** + * A service has been removed. + * + * @param event + * The ServiceEvent providing the name and fully qualified type of the service. + */ + void serviceRemoved(ServiceEvent event) { + String qualifiedName = event.getName() + "." + event.getType(); + if (_addedServices.remove(qualifiedName, _addedServices.get(qualifiedName))) { + this.getListener().serviceRemoved(event); + } else { + logger.finer("Service Removed called for a service already removed: " + event); + } + } + + /** + * A service has been resolved. Its details are now available in the ServiceInfo record.<br/> + * <b>Note:</b>This call back will never be called if the service does not resolve.<br/> + * + * @param event + * The ServiceEvent providing the name, the fully qualified type of the service, and the service info record. + */ + synchronized void serviceResolved(ServiceEvent event) { + ServiceInfo info = event.getInfo(); + if ((info != null) && (info.hasData())) { + String qualifiedName = event.getName() + "." + event.getType(); + ServiceInfo previousServiceInfo = _addedServices.get(qualifiedName); + if (!_sameInfo(info, previousServiceInfo)) { + if (null == previousServiceInfo) { + if (null == _addedServices.putIfAbsent(qualifiedName, info.clone())) { + this.getListener().serviceResolved(event); + } + } else { + if (_addedServices.replace(qualifiedName, previousServiceInfo, info.clone())) { + this.getListener().serviceResolved(event); + } + } + } else { + logger.finer("Service Resolved called for a service already resolved: " + event); + } + } else { + logger.warning("Service Resolved called for an unresolved event: " + event); + + } + } + + private static final boolean _sameInfo(ServiceInfo info, ServiceInfo lastInfo) { + if (info == null) return false; + if (lastInfo == null) return false; + if (!info.equals(lastInfo)) return false; + byte[] text = info.getTextBytes(); + byte[] lastText = lastInfo.getTextBytes(); + if (text.length != lastText.length) return false; + for (int i = 0; i < text.length; i++) { + if (text[i] != lastText[i]) return false; + } + return true; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + StringBuilder aLog = new StringBuilder(2048); + aLog.append("[Status for "); + aLog.append(this.getListener().toString()); + if (_addedServices.isEmpty()) { + aLog.append(" no type event "); + } else { + aLog.append(" ("); + for (String service : _addedServices.keySet()) { + aLog.append(service + ", "); + } + aLog.append(") "); + } + aLog.append("]"); + return aLog.toString(); + } + + } + + public static class ServiceTypeListenerStatus extends ListenerStatus<ServiceTypeListener> { + private static Logger logger = Logger.getLogger(ServiceTypeListenerStatus.class.getName()); + + private final ConcurrentMap<String, String> _addedTypes; + + /** + * @param listener + * listener being tracked. + * @param synch + * true if that listener can be called asynchronously + */ + public ServiceTypeListenerStatus(ServiceTypeListener listener, boolean synch) { + super(listener, synch); + _addedTypes = new ConcurrentHashMap<String, String>(32); + } + + /** + * A new service type was discovered. + * + * @param event + * The service event providing the fully qualified type of the service. + */ + void serviceTypeAdded(ServiceEvent event) { + if (null == _addedTypes.putIfAbsent(event.getType(), event.getType())) { + this.getListener().serviceTypeAdded(event); + } else { + logger.finest("Service Type Added called for a service type already added: " + event); + } + } + + /** + * A new subtype for the service type was discovered. + * + * <pre> + * <sub>._sub.<app>.<protocol>.<servicedomain>.<parentdomain>. + * </pre> + * + * @param event + * The service event providing the fully qualified type of the service with subtype. + */ + void subTypeForServiceTypeAdded(ServiceEvent event) { + if (null == _addedTypes.putIfAbsent(event.getType(), event.getType())) { + this.getListener().subTypeForServiceTypeAdded(event); + } else { + logger.finest("Service Sub Type Added called for a service sub type already added: " + event); + } + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + StringBuilder aLog = new StringBuilder(2048); + aLog.append("[Status for "); + aLog.append(this.getListener().toString()); + if (_addedTypes.isEmpty()) { + aLog.append(" no type event "); + } else { + aLog.append(" ("); + for (String type : _addedTypes.keySet()) { + aLog.append(type + ", "); + } + aLog.append(") "); + } + aLog.append("]"); + return aLog.toString(); + } + + } + + public final static boolean SYNCHONEOUS = true; + public final static boolean ASYNCHONEOUS = false; + + private final T _listener; + + private final boolean _synch; + + /** + * @param listener + * listener being tracked. + * @param synch + * true if that listener can be called asynchronously + */ + public ListenerStatus(T listener, boolean synch) { + super(); + _listener = listener; + _synch = synch; + } + + /** + * @return the listener + */ + public T getListener() { + return _listener; + } + + /** + * Return <cod>true</code> if the listener must be called synchronously. + * + * @return the synch + */ + public boolean isSynchronous() { + return _synch; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return this.getListener().hashCode(); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + return (obj instanceof ListenerStatus) && this.getListener().equals(((ListenerStatus<?>) obj).getListener()); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "[Status for " + this.getListener().toString() + "]"; + } +} |