/** * */ 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.
* The main purpose of this class is to collapse consecutive events so that we can guarantee the correct call back sequence. * * @param * listener type */ public class ListenerStatus { public static class ServiceListenerStatus extends ListenerStatus { private static Logger logger = Logger.getLogger(ServiceListenerStatus.class.getName()); private final ConcurrentMap _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(32); } /** * A service has been added.
* Note:This event is only the service added event. The service info associated with this event does not include resolution information.
* To get the full resolved information you need to listen to {@link #serviceResolved(ServiceEvent)} or call {@link JmDNS#getServiceInfo(String, String, long)} * *
         *  ServiceInfo info = event.getDNS().getServiceInfo(event.getType(), event.getName())
         * 
*

* Please note that service resolution may take a few second to resolve. *

* * @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.
* Note:This call back will never be called if the service does not resolve.
* * @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 { private static Logger logger = Logger.getLogger(ServiceTypeListenerStatus.class.getName()); private final ConcurrentMap _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(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. * *
         * <sub>._sub.<app>.<protocol>.<servicedomain>.<parentdomain>.
         * 
* * @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 true 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() + "]"; } }