13742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// /Copyright 2003-2005 Arthur van Hoff, Rick Blair
23742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// Licensed under Apache License version 2.0
33742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// Original license LGPL
43742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
53742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanpackage javax.jmdns.impl;
63742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
73742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.io.IOException;
83742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.net.DatagramPacket;
93742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.net.Inet4Address;
103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.net.Inet6Address;
113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.net.InetAddress;
123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.net.MulticastSocket;
133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.net.SocketException;
143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.AbstractMap;
153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.ArrayList;
163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.Collection;
173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.Collections;
183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.HashMap;
193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.HashSet;
203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.Iterator;
213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.LinkedList;
223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.List;
233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.Map;
243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.Properties;
253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.Random;
263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.Set;
273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.concurrent.ConcurrentHashMap;
283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.concurrent.ConcurrentMap;
293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.concurrent.ExecutorService;
303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.concurrent.Executors;
313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.concurrent.locks.ReentrantLock;
323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.logging.Level;
333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.logging.Logger;
343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.JmDNS;
363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.ServiceEvent;
373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.ServiceInfo;
383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.ServiceInfo.Fields;
393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.ServiceListener;
403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.ServiceTypeListener;
413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.ListenerStatus.ServiceListenerStatus;
423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.ListenerStatus.ServiceTypeListenerStatus;
433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.constants.DNSConstants;
443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.constants.DNSRecordClass;
453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.constants.DNSRecordType;
463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.constants.DNSState;
473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.tasks.DNSTask;
483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// REMIND: multiple IP addresses
503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman/**
523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman * mDNS implementation in Java.
533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman *
543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman * @author Arthur van Hoff, Rick Blair, Jeff Sonstein, Werner Randelshofer, Pierre Frisch, Scott Lewis
553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman */
563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanpublic class JmDNSImpl extends JmDNS implements DNSStatefulObject, DNSTaskStarter {
573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private static Logger logger = Logger.getLogger(JmDNSImpl.class.getName());
583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public enum Operation {
603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        Remove, Update, Add, RegisterServiceType, Noop
613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * This is the multicast group, we are listening to for multicast DNS messages.
653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private volatile InetAddress                                     _group;
673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * This is our multicast socket.
693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private volatile MulticastSocket                                 _socket;
713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Holds instances of JmDNS.DNSListener. Must by a synchronized collection, because it is updated from concurrent threads.
743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private final List<DNSListener>                                  _listeners;
763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Holds instances of ServiceListener's. Keys are Strings holding a fully qualified service type. Values are LinkedList's of ServiceListener's.
793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private final ConcurrentMap<String, List<ServiceListenerStatus>> _serviceListeners;
813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Holds instances of ServiceTypeListener's.
843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private final Set<ServiceTypeListenerStatus>                     _typeListeners;
863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Cache for DNSEntry's.
893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private final DNSCache                                           _cache;
913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * This hashtable holds the services that have been registered. Keys are instances of String which hold an all lower-case version of the fully qualified service name. Values are instances of ServiceInfo.
943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private final ConcurrentMap<String, ServiceInfo>                 _services;
963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * This hashtable holds the service types that have been registered or that have been received in an incoming datagram.<br/>
993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Keys are instances of String which hold an all lower-case version of the fully qualified service type.<br/>
1003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Values hold the fully qualified service type.
1013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
1023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private final ConcurrentMap<String, ServiceTypeEntry>            _serviceTypes;
1033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private volatile Delegate                                        _delegate;
1053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
1073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * This is used to store type entries. The type is stored as a call variable and the map support the subtypes.
1083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * <p>
1093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * The key is the lowercase version as the value is the case preserved version.
1103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * </p>
1113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
1123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public static class ServiceTypeEntry extends AbstractMap<String, String> implements Cloneable {
1133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        private final Set<Map.Entry<String, String>> _entrySet;
1153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        private final String                         _type;
1173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        private static class SubTypeEntry implements Entry<String, String>, java.io.Serializable, Cloneable {
1193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            private static final long serialVersionUID = 9188503522395855322L;
1213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            private final String      _key;
1233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            private final String      _value;
1243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            public SubTypeEntry(String subtype) {
1263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                super();
1273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                _value = (subtype != null ? subtype : "");
1283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                _key = _value.toLowerCase();
1293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
1303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            /**
1323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman             * {@inheritDoc}
1333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman             */
1343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            @Override
1353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            public String getKey() {
1363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                return _key;
1373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
1383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            /**
1403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman             * {@inheritDoc}
1413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman             */
1423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            @Override
1433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            public String getValue() {
1443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                return _value;
1453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
1463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            /**
1483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman             * Replaces the value corresponding to this entry with the specified value (optional operation). This implementation simply throws <tt>UnsupportedOperationException</tt>, as this class implements an <i>immutable</i> map entry.
1493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman             *
1503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman             * @param value
1513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman             *            new value to be stored in this entry
1523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman             * @return (Does not return)
1533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman             * @exception UnsupportedOperationException
1543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman             *                always
1553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman             */
1563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            @Override
1573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            public String setValue(String value) {
1583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                throw new UnsupportedOperationException();
1593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
1603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            /**
1623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman             * {@inheritDoc}
1633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman             */
1643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            @Override
1653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            public boolean equals(Object entry) {
1663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (!(entry instanceof Map.Entry)) {
1673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    return false;
1683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
1693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                return this.getKey().equals(((Map.Entry<?, ?>) entry).getKey()) && this.getValue().equals(((Map.Entry<?, ?>) entry).getValue());
1703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
1713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            /**
1733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman             * {@inheritDoc}
1743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman             */
1753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            @Override
1763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            public int hashCode() {
1773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                return (_key == null ? 0 : _key.hashCode()) ^ (_value == null ? 0 : _value.hashCode());
1783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
1793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            /*
1813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman             * (non-Javadoc)
1823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman             * @see java.lang.Object#clone()
1833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman             */
1843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            @Override
1853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            public SubTypeEntry clone() {
1863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                // Immutable object
1873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                return this;
1883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
1893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            /**
1913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman             * {@inheritDoc}
1923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman             */
1933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            @Override
1943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            public String toString() {
1953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                return _key + "=" + _value;
1963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
1973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
1993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
2003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        public ServiceTypeEntry(String type) {
2013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            super();
2023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this._type = type;
2033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this._entrySet = new HashSet<Map.Entry<String, String>>();
2043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
2053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
2063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        /**
2073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * The type associated with this entry.
2083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         *
2093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * @return the type
2103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         */
2113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        public String getType() {
2123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            return _type;
2133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
2143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
2153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        /*
2163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * (non-Javadoc)
2173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * @see java.util.AbstractMap#entrySet()
2183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         */
2193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        @Override
2203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        public Set<Map.Entry<String, String>> entrySet() {
2213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            return _entrySet;
2223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
2233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
2243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        /**
2253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * Returns <code>true</code> if this set contains the specified element. More formally, returns <code>true</code> if and only if this set contains an element <code>e</code> such that
2263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * <code>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</code>.
2273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         *
2283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * @param subtype
2293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         *            element whose presence in this set is to be tested
2303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * @return <code>true</code> if this set contains the specified element
2313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         */
2323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        public boolean contains(String subtype) {
2333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            return subtype != null && this.containsKey(subtype.toLowerCase());
2343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
2353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
2363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        /**
2373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * Adds the specified element to this set if it is not already present. More formally, adds the specified element <code>e</code> to this set if this set contains no element <code>e2</code> such that
2383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * <code>(e==null&nbsp;?&nbsp;e2==null&nbsp;:&nbsp;e.equals(e2))</code>. If this set already contains the element, the call leaves the set unchanged and returns <code>false</code>.
2393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         *
2403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * @param subtype
2413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         *            element to be added to this set
2423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * @return <code>true</code> if this set did not already contain the specified element
2433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         */
2443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        public boolean add(String subtype) {
2453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (subtype == null || this.contains(subtype)) {
2463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                return false;
2473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
2483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            _entrySet.add(new SubTypeEntry(subtype));
2493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            return true;
2503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
2513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
2523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        /**
2533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * Returns an iterator over the elements in this set. The elements are returned in no particular order (unless this set is an instance of some class that provides a guarantee).
2543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         *
2553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * @return an iterator over the elements in this set
2563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         */
2573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        public Iterator<String> iterator() {
2583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            return this.keySet().iterator();
2593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
2603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
2613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        /*
2623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * (non-Javadoc)
2633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * @see java.util.AbstractMap#clone()
2643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         */
2653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        @Override
2663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        public ServiceTypeEntry clone() {
2673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            ServiceTypeEntry entry = new ServiceTypeEntry(this.getType());
2683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            for (Map.Entry<String, String> subTypeEntry : this.entrySet()) {
2693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                entry.add(subTypeEntry.getValue());
2703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
2713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            return entry;
2723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
2733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
2743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        /*
2753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * (non-Javadoc)
2763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * @see java.util.AbstractMap#toString()
2773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         */
2783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        @Override
2793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        public String toString() {
2803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            final StringBuilder aLog = new StringBuilder(200);
2813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (this.isEmpty()) {
2823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                aLog.append("empty");
2833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            } else {
2843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                for (String value : this.values()) {
2853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    aLog.append(value);
2863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    aLog.append(", ");
2873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
2883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                aLog.setLength(aLog.length() - 2);
2893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
2903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            return aLog.toString();
2913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
2923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
2933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
2943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
2953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
2963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * This is the shutdown hook, we registered with the java runtime.
2973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
2983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    protected Thread                                      _shutdown;
2993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
3003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
3013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Handle on the local host
3023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
3033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private HostInfo                                      _localHost;
3043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
3053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private Thread                                        _incomingListener;
3063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
3073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
3083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Throttle count. This is used to count the overall number of probes sent by JmDNS. When the last throttle increment happened .
3093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
3103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private int                                           _throttle;
3113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
3123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
3133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Last throttle increment.
3143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
3153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private long                                          _lastThrottleIncrement;
3163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
3173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private final ExecutorService                         _executor = Executors.newSingleThreadExecutor();
3183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
3193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    //
3203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    // 2009-09-16 ldeck: adding docbug patch with slight ammendments
3213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    // 'Fixes two deadlock conditions involving JmDNS.close() - ID: 1473279'
3223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    //
3233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    // ---------------------------------------------------
3243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
3253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * The timer that triggers our announcements. We can't use the main timer object, because that could cause a deadlock where Prober waits on JmDNS.this lock held by close(), close() waits for us to finish, and we wait for Prober to give us back
3263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * the timer thread so we can announce. (Patch from docbug in 2006-04-19 still wasn't patched .. so I'm doing it!)
3273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
3283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    // private final Timer _cancelerTimer;
3293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    // ---------------------------------------------------
3303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
3313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
3323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * The source for random values. This is used to introduce random delays in responses. This reduces the potential for collisions on the network.
3333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
3343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private final static Random                           _random   = new Random();
3353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
3363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
3373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * This lock is used to coordinate processing of incoming and outgoing messages. This is needed, because the Rendezvous Conformance Test does not forgive race conditions.
3383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
3393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private final ReentrantLock                           _ioLock   = new ReentrantLock();
3403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
3413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
3423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * If an incoming package which needs an answer is truncated, we store it here. We add more incoming DNSRecords to it, until the JmDNS.Responder timer picks it up.<br/>
3433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * FIXME [PJYF June 8 2010]: This does not work well with multiple planned answers for packages that came in from different clients.
3443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
3453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private DNSIncoming                                   _plannedAnswer;
3463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
3473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    // State machine
3483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
3493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
3503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * This hashtable is used to maintain a list of service types being collected by this JmDNS instance. The key of the hashtable is a service type name, the value is an instance of JmDNS.ServiceCollector.
3513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
3523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @see #list
3533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
3543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private final ConcurrentMap<String, ServiceCollector> _serviceCollectors;
3553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
3563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private final String                                  _name;
3573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
3583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
3593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Main method to display API information if run from java -jar
3603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
3613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param argv
3623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *            the command line arguments
3633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
3643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public static void main(String[] argv) {
3653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        String version = null;
3663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        try {
3673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            final Properties pomProperties = new Properties();
3683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            pomProperties.load(JmDNSImpl.class.getResourceAsStream("/META-INF/maven/javax.jmdns/jmdns/pom.properties"));
3693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            version = pomProperties.getProperty("version");
3703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        } catch (Exception e) {
3713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            version = "RUNNING.IN.IDE.FULL";
3723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
3733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        System.out.println("JmDNS version \"" + version + "\"");
3743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        System.out.println(" ");
3753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
3763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        System.out.println("Running on java version \"" + System.getProperty("java.version") + "\"" + " (build " + System.getProperty("java.runtime.version") + ")" + " from " + System.getProperty("java.vendor"));
3773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
3783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        System.out.println("Operating environment \"" + System.getProperty("os.name") + "\"" + " version " + System.getProperty("os.version") + " on " + System.getProperty("os.arch"));
3793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
3803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        System.out.println("For more information on JmDNS please visit https://sourceforge.net/projects/jmdns/");
3813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
3823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
3833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
3843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Create an instance of JmDNS and bind it to a specific network interface given its IP-address.
3853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
3863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param address
3873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *            IP address to bind to.
3883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param name
3893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *            name of the newly created JmDNS
3903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @exception IOException
3913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
3923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public JmDNSImpl(InetAddress address, String name) throws IOException {
3933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        super();
3943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (logger.isLoggable(Level.FINER)) {
3953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            logger.finer("JmDNS instance created");
3963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
3973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        _cache = new DNSCache(100);
3983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
3993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        _listeners = Collections.synchronizedList(new ArrayList<DNSListener>());
4003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        _serviceListeners = new ConcurrentHashMap<String, List<ServiceListenerStatus>>();
4013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        _typeListeners = Collections.synchronizedSet(new HashSet<ServiceTypeListenerStatus>());
4023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        _serviceCollectors = new ConcurrentHashMap<String, ServiceCollector>();
4033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
4043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        _services = new ConcurrentHashMap<String, ServiceInfo>(20);
4053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        _serviceTypes = new ConcurrentHashMap<String, ServiceTypeEntry>(20);
4063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
4073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        _localHost = HostInfo.newHostInfo(address, this, name);
4083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        _name = (name != null ? name : _localHost.getName());
4093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
4103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // _cancelerTimer = new Timer("JmDNS.cancelerTimer");
4113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
4123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // (ldeck 2.1.1) preventing shutdown blocking thread
4133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // -------------------------------------------------
4143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // _shutdown = new Thread(new Shutdown(), "JmDNS.Shutdown");
4153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // Runtime.getRuntime().addShutdownHook(_shutdown);
4163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
4173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // -------------------------------------------------
4183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
4193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // Bind to multicast socket
4203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.openMulticastSocket(this.getLocalHost());
4213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.start(this.getServices().values());
4223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
4233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.startReaper();
4243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
4253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
4263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private void start(Collection<? extends ServiceInfo> serviceInfos) {
4273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (_incomingListener == null) {
4283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            _incomingListener = new SocketListener(this);
4293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            _incomingListener.start();
4303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
4313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.startProber();
4323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        for (ServiceInfo info : serviceInfos) {
4333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            try {
4343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                this.registerService(new ServiceInfoImpl(info));
4353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            } catch (final Exception exception) {
4363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                logger.log(Level.WARNING, "start() Registration exception ", exception);
4373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
4383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
4393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
4403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
4413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private void openMulticastSocket(HostInfo hostInfo) throws IOException {
4423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (_group == null) {
4433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (hostInfo.getInetAddress() instanceof Inet6Address) {
4443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                _group = InetAddress.getByName(DNSConstants.MDNS_GROUP_IPV6);
4453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            } else {
4463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                _group = InetAddress.getByName(DNSConstants.MDNS_GROUP);
4473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
4483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
4493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (_socket != null) {
4503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this.closeMulticastSocket();
4513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
4523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        _socket = new MulticastSocket(DNSConstants.MDNS_PORT);
4533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if ((hostInfo != null) && (hostInfo.getInterface() != null)) {
4543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            try {
4553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                _socket.setNetworkInterface(hostInfo.getInterface());
4563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            } catch (SocketException e) {
4573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (logger.isLoggable(Level.FINE)) {
4583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    logger.fine("openMulticastSocket() Set network interface exception: " + e.getMessage());
4593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
4603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
4613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
4623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        _socket.setTimeToLive(255);
4633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        _socket.joinGroup(_group);
4643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
4653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
4663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private void closeMulticastSocket() {
4673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // jP: 20010-01-18. See below. We'll need this monitor...
4683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // assert (Thread.holdsLock(this));
4693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (logger.isLoggable(Level.FINER)) {
4703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            logger.finer("closeMulticastSocket()");
4713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
4723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (_socket != null) {
4733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            // close socket
4743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            try {
4753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                try {
4763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    _socket.leaveGroup(_group);
4773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                } catch (SocketException exception) {
4783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    //
4793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
4803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                _socket.close();
4813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                // jP: 20010-01-18. It isn't safe to join() on the listener
4823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                // thread - it attempts to lock the IoLock object, and deadlock
4833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                // ensues. Per issue #2933183, changed this to wait on the JmDNS
4843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                // monitor, checking on each notify (or timeout) that the
4853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                // listener thread has stopped.
4863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                //
4873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                while (_incomingListener != null && _incomingListener.isAlive()) {
4883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    synchronized (this) {
4893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        try {
4903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            if (_incomingListener != null && _incomingListener.isAlive()) {
4913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                // wait time is arbitrary, we're really expecting notification.
4923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                if (logger.isLoggable(Level.FINER)) {
4933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                    logger.finer("closeMulticastSocket(): waiting for jmDNS monitor");
4943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                }
4953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                this.wait(1000);
4963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            }
4973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        } catch (InterruptedException ignored) {
4983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            // Ignored
4993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        }
5003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    }
5013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
5023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                _incomingListener = null;
5033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            } catch (final Exception exception) {
5043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                logger.log(Level.WARNING, "closeMulticastSocket() Close socket exception ", exception);
5053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
5063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            _socket = null;
5073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
5083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
5093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
5103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    // State machine
5113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
5123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
5133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
5143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
5153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public boolean advanceState(DNSTask task) {
5163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return this._localHost.advanceState(task);
5173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
5183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
5193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
5203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
5213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
5223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
5233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public boolean revertState() {
5243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return this._localHost.revertState();
5253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
5263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
5273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
5283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
5293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
5303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
5313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public boolean cancelState() {
5323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return this._localHost.cancelState();
5333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
5343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
5353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
5363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
5373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
5383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
5393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public boolean closeState() {
5403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return this._localHost.closeState();
5413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
5423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
5433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
5443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
5453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
5463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
5473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public boolean recoverState() {
5483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return this._localHost.recoverState();
5493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
5503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
5513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
5523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
5533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
5543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
5553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public JmDNSImpl getDns() {
5563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return this;
5573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
5583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
5593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
5603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
5613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
5623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
5633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void associateWithTask(DNSTask task, DNSState state) {
5643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this._localHost.associateWithTask(task, state);
5653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
5663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
5673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
5683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
5693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
5703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
5713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void removeAssociationWithTask(DNSTask task) {
5723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this._localHost.removeAssociationWithTask(task);
5733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
5743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
5753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
5763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
5773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
5783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
5793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public boolean isAssociatedWithTask(DNSTask task, DNSState state) {
5803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return this._localHost.isAssociatedWithTask(task, state);
5813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
5823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
5833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
5843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
5853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
5863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
5873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public boolean isProbing() {
5883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return this._localHost.isProbing();
5893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
5903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
5913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
5923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
5933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
5943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
5953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public boolean isAnnouncing() {
5963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return this._localHost.isAnnouncing();
5973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
5983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
5993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
6003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
6013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
6023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
6033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public boolean isAnnounced() {
6043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return this._localHost.isAnnounced();
6053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
6063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
6073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
6083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
6093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
6103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
6113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public boolean isCanceling() {
6123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return this._localHost.isCanceling();
6133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
6143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
6153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
6163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
6173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
6183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
6193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public boolean isCanceled() {
6203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return this._localHost.isCanceled();
6213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
6223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
6233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
6243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
6253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
6263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
6273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public boolean isClosing() {
6283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return this._localHost.isClosing();
6293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
6303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
6313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
6323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
6333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
6343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
6353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public boolean isClosed() {
6363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return this._localHost.isClosed();
6373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
6383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
6393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
6403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
6413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
6423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
6433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public boolean waitForAnnounced(long timeout) {
6443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return this._localHost.waitForAnnounced(timeout);
6453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
6463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
6473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
6483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
6493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
6503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
6513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public boolean waitForCanceled(long timeout) {
6523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return this._localHost.waitForCanceled(timeout);
6533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
6543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
6553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
6563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Return the DNSCache associated with the cache variable
6573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
6583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @return DNS cache
6593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
6603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public DNSCache getCache() {
6613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return _cache;
6623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
6633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
6643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
6653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
6663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
6673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
6683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public String getName() {
6693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return _name;
6703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
6713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
6723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
6733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
6743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
6753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
6763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public String getHostName() {
6773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return _localHost.getName();
6783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
6793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
6803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
6813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Returns the local host info
6823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
6833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @return local host info
6843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
6853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public HostInfo getLocalHost() {
6863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return _localHost;
6873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
6883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
6893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
6903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
6913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
6923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
6933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public InetAddress getInetAddress() throws IOException {
6943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return _localHost.getInetAddress();
6953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
6963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
6973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
6983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
6993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
7003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
7013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Deprecated
7023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public InetAddress getInterface() throws IOException {
7033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return _socket.getInterface();
7043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
7053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
7063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
7073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
7083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
7093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
7103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public ServiceInfo getServiceInfo(String type, String name) {
7113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return this.getServiceInfo(type, name, false, DNSConstants.SERVICE_INFO_TIMEOUT);
7123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
7133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
7143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
7153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
7163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
7173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
7183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public ServiceInfo getServiceInfo(String type, String name, long timeout) {
7193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return this.getServiceInfo(type, name, false, timeout);
7203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
7213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
7223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
7233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
7243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
7253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
7263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public ServiceInfo getServiceInfo(String type, String name, boolean persistent) {
7273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return this.getServiceInfo(type, name, persistent, DNSConstants.SERVICE_INFO_TIMEOUT);
7283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
7293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
7303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
7313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
7323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
7333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
7343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public ServiceInfo getServiceInfo(String type, String name, boolean persistent, long timeout) {
7353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        final ServiceInfoImpl info = this.resolveServiceInfo(type, name, "", persistent);
7363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.waitForInfoData(info, timeout);
7373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return (info.hasData() ? info : null);
7383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
7393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
7403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    ServiceInfoImpl resolveServiceInfo(String type, String name, String subtype, boolean persistent) {
7413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.cleanCache();
7423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        String loType = type.toLowerCase();
7433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.registerServiceType(type);
7443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (_serviceCollectors.putIfAbsent(loType, new ServiceCollector(type)) == null) {
7453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this.addServiceListener(loType, _serviceCollectors.get(loType), ListenerStatus.SYNCHONEOUS);
7463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
7473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
7483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // Check if the answer is in the cache.
7493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        final ServiceInfoImpl info = this.getServiceInfoFromCache(type, name, subtype, persistent);
7503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // We still run the resolver to do the dispatch but if the info is already there it will quit immediately
7513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.startServiceInfoResolver(info);
7523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
7533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return info;
7543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
7553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
7563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    ServiceInfoImpl getServiceInfoFromCache(String type, String name, String subtype, boolean persistent) {
7573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // Check if the answer is in the cache.
7583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        ServiceInfoImpl info = new ServiceInfoImpl(type, name, subtype, 0, 0, 0, persistent, (byte[]) null);
7593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        DNSEntry pointerEntry = this.getCache().getDNSEntry(new DNSRecord.Pointer(type, DNSRecordClass.CLASS_ANY, false, 0, info.getQualifiedName()));
7603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (pointerEntry instanceof DNSRecord) {
7613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            ServiceInfoImpl cachedInfo = (ServiceInfoImpl) ((DNSRecord) pointerEntry).getServiceInfo(persistent);
7623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (cachedInfo != null) {
7633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                // To get a complete info record we need to retrieve the service, address and the text bytes.
7643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
7653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                Map<Fields, String> map = cachedInfo.getQualifiedNameMap();
7663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                byte[] srvBytes = null;
7673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                String server = "";
7683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                DNSEntry serviceEntry = this.getCache().getDNSEntry(info.getQualifiedName(), DNSRecordType.TYPE_SRV, DNSRecordClass.CLASS_ANY);
7693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (serviceEntry instanceof DNSRecord) {
7703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    ServiceInfo cachedServiceEntryInfo = ((DNSRecord) serviceEntry).getServiceInfo(persistent);
7713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    if (cachedServiceEntryInfo != null) {
7723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        cachedInfo = new ServiceInfoImpl(map, cachedServiceEntryInfo.getPort(), cachedServiceEntryInfo.getWeight(), cachedServiceEntryInfo.getPriority(), persistent, (byte[]) null);
7733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        srvBytes = cachedServiceEntryInfo.getTextBytes();
7743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        server = cachedServiceEntryInfo.getServer();
7753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    }
7763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
7773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                DNSEntry addressEntry = this.getCache().getDNSEntry(server, DNSRecordType.TYPE_A, DNSRecordClass.CLASS_ANY);
7783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (addressEntry instanceof DNSRecord) {
7793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    ServiceInfo cachedAddressInfo = ((DNSRecord) addressEntry).getServiceInfo(persistent);
7803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    if (cachedAddressInfo != null) {
7813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        for (Inet4Address address : cachedAddressInfo.getInet4Addresses()) {
7823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            cachedInfo.addAddress(address);
7833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        }
7843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        cachedInfo._setText(cachedAddressInfo.getTextBytes());
7853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    }
7863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
7873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                addressEntry = this.getCache().getDNSEntry(server, DNSRecordType.TYPE_AAAA, DNSRecordClass.CLASS_ANY);
7883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (addressEntry instanceof DNSRecord) {
7893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    ServiceInfo cachedAddressInfo = ((DNSRecord) addressEntry).getServiceInfo(persistent);
7903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    if (cachedAddressInfo != null) {
7913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        for (Inet6Address address : cachedAddressInfo.getInet6Addresses()) {
7923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            cachedInfo.addAddress(address);
7933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        }
7943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        cachedInfo._setText(cachedAddressInfo.getTextBytes());
7953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    }
7963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
7973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                DNSEntry textEntry = this.getCache().getDNSEntry(cachedInfo.getQualifiedName(), DNSRecordType.TYPE_TXT, DNSRecordClass.CLASS_ANY);
7983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (textEntry instanceof DNSRecord) {
7993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    ServiceInfo cachedTextInfo = ((DNSRecord) textEntry).getServiceInfo(persistent);
8003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    if (cachedTextInfo != null) {
8013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        cachedInfo._setText(cachedTextInfo.getTextBytes());
8023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    }
8033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
8043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (cachedInfo.getTextBytes().length == 0) {
8053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    cachedInfo._setText(srvBytes);
8063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
8073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (cachedInfo.hasData()) {
8083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    info = cachedInfo;
8093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
8103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
8113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
8123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return info;
8133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
8143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
8153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private void waitForInfoData(ServiceInfo info, long timeout) {
8163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        synchronized (info) {
8173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            long loops = (timeout / 200L);
8183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (loops < 1) {
8193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                loops = 1;
8203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
8213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            for (int i = 0; i < loops; i++) {
8223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (info.hasData()) {
8233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    break;
8243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
8253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                try {
8263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    info.wait(200);
8273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                } catch (final InterruptedException e) {
8283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    /* Stub */
8293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
8303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
8313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
8323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
8333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
8343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
8353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
8363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
8373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
8383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void requestServiceInfo(String type, String name) {
8393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.requestServiceInfo(type, name, false, DNSConstants.SERVICE_INFO_TIMEOUT);
8403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
8413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
8423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
8433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
8443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
8453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
8463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void requestServiceInfo(String type, String name, boolean persistent) {
8473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.requestServiceInfo(type, name, persistent, DNSConstants.SERVICE_INFO_TIMEOUT);
8483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
8493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
8503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
8513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
8523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
8533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
8543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void requestServiceInfo(String type, String name, long timeout) {
8553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.requestServiceInfo(type, name, false, DNSConstants.SERVICE_INFO_TIMEOUT);
8563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
8573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
8583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
8593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
8603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
8613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
8623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void requestServiceInfo(String type, String name, boolean persistent, long timeout) {
8633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        final ServiceInfoImpl info = this.resolveServiceInfo(type, name, "", persistent);
8643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.waitForInfoData(info, timeout);
8653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
8663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
8673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    void handleServiceResolved(ServiceEvent event) {
8683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        List<ServiceListenerStatus> list = _serviceListeners.get(event.getType().toLowerCase());
8693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        final List<ServiceListenerStatus> listCopy;
8703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if ((list != null) && (!list.isEmpty())) {
8713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if ((event.getInfo() != null) && event.getInfo().hasData()) {
8723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                final ServiceEvent localEvent = event;
8733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                synchronized (list) {
8743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    listCopy = new ArrayList<ServiceListenerStatus>(list);
8753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
8763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                for (final ServiceListenerStatus listener : listCopy) {
8773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    _executor.submit(new Runnable() {
8783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        /** {@inheritDoc} */
8793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        @Override
8803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        public void run() {
8813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            listener.serviceResolved(localEvent);
8823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        }
8833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    });
8843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
8853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
8863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
8873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
8883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
8893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
8903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
8913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
8923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
8933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void addServiceTypeListener(ServiceTypeListener listener) throws IOException {
8943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        ServiceTypeListenerStatus status = new ServiceTypeListenerStatus(listener, ListenerStatus.ASYNCHONEOUS);
8953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        _typeListeners.add(status);
8963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
8973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // report cached service types
8983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        for (String type : _serviceTypes.keySet()) {
8993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            status.serviceTypeAdded(new ServiceEventImpl(this, type, "", null));
9003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
9013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
9023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.startTypeResolver();
9033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
9043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
9053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
9063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
9073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
9083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
9093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void removeServiceTypeListener(ServiceTypeListener listener) {
9103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        ServiceTypeListenerStatus status = new ServiceTypeListenerStatus(listener, ListenerStatus.ASYNCHONEOUS);
9113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        _typeListeners.remove(status);
9123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
9133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
9143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
9153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
9163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
9173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
9183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void addServiceListener(String type, ServiceListener listener) {
9193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.addServiceListener(type, listener, ListenerStatus.ASYNCHONEOUS);
9203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
9213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
9223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private void addServiceListener(String type, ServiceListener listener, boolean synch) {
9233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        ServiceListenerStatus status = new ServiceListenerStatus(listener, synch);
9243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        final String loType = type.toLowerCase();
9253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        List<ServiceListenerStatus> list = _serviceListeners.get(loType);
9263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (list == null) {
9273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (_serviceListeners.putIfAbsent(loType, new LinkedList<ServiceListenerStatus>()) == null) {
9283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (_serviceCollectors.putIfAbsent(loType, new ServiceCollector(type)) == null) {
9293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    // We have a problem here. The service collectors must be called synchronously so that their cache get cleaned up immediately or we will report .
9303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    this.addServiceListener(loType, _serviceCollectors.get(loType), ListenerStatus.SYNCHONEOUS);
9313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
9323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
9333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            list = _serviceListeners.get(loType);
9343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
9353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (list != null) {
9363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            synchronized (list) {
9373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (!list.contains(listener)) {
9383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    list.add(status);
9393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
9403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
9413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
9423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // report cached service types
9433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        final List<ServiceEvent> serviceEvents = new ArrayList<ServiceEvent>();
9443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        Collection<DNSEntry> dnsEntryLits = this.getCache().allValues();
9453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        for (DNSEntry entry : dnsEntryLits) {
9463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            final DNSRecord record = (DNSRecord) entry;
9473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (record.getRecordType() == DNSRecordType.TYPE_SRV) {
9483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (record.getKey().endsWith(loType)) {
9493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    // Do not used the record embedded method for generating event this will not work.
9503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    // serviceEvents.add(record.getServiceEvent(this));
9513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    serviceEvents.add(new ServiceEventImpl(this, record.getType(), toUnqualifiedName(record.getType(), record.getName()), record.getServiceInfo()));
9523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
9533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
9543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
9553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // Actually call listener with all service events added above
9563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        for (ServiceEvent serviceEvent : serviceEvents) {
9573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            status.serviceAdded(serviceEvent);
9583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
9593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // Create/start ServiceResolver
9603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.startServiceResolver(type);
9613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
9623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
9633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
9643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
9653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
9663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
9673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void removeServiceListener(String type, ServiceListener listener) {
9683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        String loType = type.toLowerCase();
9693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        List<ServiceListenerStatus> list = _serviceListeners.get(loType);
9703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (list != null) {
9713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            synchronized (list) {
9723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                ServiceListenerStatus status = new ServiceListenerStatus(listener, ListenerStatus.ASYNCHONEOUS);
9733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                list.remove(status);
9743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (list.isEmpty()) {
9753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    _serviceListeners.remove(loType, list);
9763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
9773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
9783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
9793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
9803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
9813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
9823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
9833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
9843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
9853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void registerService(ServiceInfo infoAbstract) throws IOException {
9863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (this.isClosing() || this.isClosed()) {
9873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            throw new IllegalStateException("This DNS is closed.");
9883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
9893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        final ServiceInfoImpl info = (ServiceInfoImpl) infoAbstract;
9903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
9913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (info.getDns() != null) {
9923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (info.getDns() != this) {
9933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                throw new IllegalStateException("A service information can only be registered with a single instamce of JmDNS.");
9943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            } else if (_services.get(info.getKey()) != null) {
9953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                throw new IllegalStateException("A service information can only be registered once.");
9963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
9973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
9983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        info.setDns(this);
9993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
10003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.registerServiceType(info.getTypeWithSubtype());
10013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
10023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // bind the service to this address
10033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        info.recoverState();
10043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        info.setServer(_localHost.getName());
10053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        info.addAddress(_localHost.getInet4Address());
10063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        info.addAddress(_localHost.getInet6Address());
10073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
10083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.waitForAnnounced(DNSConstants.SERVICE_INFO_TIMEOUT);
10093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
10103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.makeServiceNameUnique(info);
10113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        while (_services.putIfAbsent(info.getKey(), info) != null) {
10123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this.makeServiceNameUnique(info);
10133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
10143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
10153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.startProber();
10163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        info.waitForAnnounced(DNSConstants.SERVICE_INFO_TIMEOUT);
10173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
10183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (logger.isLoggable(Level.FINE)) {
10193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            logger.fine("registerService() JmDNS registered service as " + info);
10203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
10213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
10223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
10233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
10243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
10253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
10263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
10273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void unregisterService(ServiceInfo infoAbstract) {
10283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        final ServiceInfoImpl info = (ServiceInfoImpl) _services.get(infoAbstract.getKey());
10293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
10303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (info != null) {
10313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            info.cancelState();
10323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this.startCanceler();
10333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            info.waitForCanceled(DNSConstants.CLOSE_TIMEOUT);
10343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
10353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            _services.remove(info.getKey(), info);
10363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (logger.isLoggable(Level.FINE)) {
10373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                logger.fine("unregisterService() JmDNS unregistered service as " + info);
10383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
10393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        } else {
10403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            logger.warning("Removing unregistered service info: " + infoAbstract.getKey());
10413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
10423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
10433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
10443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
10453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
10463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
10473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
10483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void unregisterAllServices() {
10493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (logger.isLoggable(Level.FINER)) {
10503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            logger.finer("unregisterAllServices()");
10513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
10523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
10533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        for (String name : _services.keySet()) {
10543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            ServiceInfoImpl info = (ServiceInfoImpl) _services.get(name);
10553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (info != null) {
10563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (logger.isLoggable(Level.FINER)) {
10573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    logger.finer("Cancelling service info: " + info);
10583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
10593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                info.cancelState();
10603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
10613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
10623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.startCanceler();
10633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
10643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        for (String name : _services.keySet()) {
10653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            ServiceInfoImpl info = (ServiceInfoImpl) _services.get(name);
10663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (info != null) {
10673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (logger.isLoggable(Level.FINER)) {
10683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    logger.finer("Wait for service info cancel: " + info);
10693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
10703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                info.waitForCanceled(DNSConstants.CLOSE_TIMEOUT);
10713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                _services.remove(name, info);
10723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
10733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
10743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
10753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
10763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
10773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
10783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
10793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
10803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
10813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public boolean registerServiceType(String type) {
10823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        boolean typeAdded = false;
10833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        Map<Fields, String> map = ServiceInfoImpl.decodeQualifiedNameMapForType(type);
10843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        String domain = map.get(Fields.Domain);
10853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        String protocol = map.get(Fields.Protocol);
10863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        String application = map.get(Fields.Application);
10873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        String subtype = map.get(Fields.Subtype);
10883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
10893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        final String name = (application.length() > 0 ? "_" + application + "." : "") + (protocol.length() > 0 ? "_" + protocol + "." : "") + domain + ".";
10903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        final String loname = name.toLowerCase();
10913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (logger.isLoggable(Level.FINE)) {
10923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            logger.fine(this.getName() + ".registering service type: " + type + " as: " + name + (subtype.length() > 0 ? " subtype: " + subtype : ""));
10933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
10943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (!_serviceTypes.containsKey(loname) && !application.toLowerCase().equals("dns-sd") && !domain.toLowerCase().endsWith("in-addr.arpa") && !domain.toLowerCase().endsWith("ip6.arpa")) {
10953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            typeAdded = _serviceTypes.putIfAbsent(loname, new ServiceTypeEntry(name)) == null;
10963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (typeAdded) {
10973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                final ServiceTypeListenerStatus[] list = _typeListeners.toArray(new ServiceTypeListenerStatus[_typeListeners.size()]);
10983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                final ServiceEvent event = new ServiceEventImpl(this, name, "", null);
10993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                for (final ServiceTypeListenerStatus status : list) {
11003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    _executor.submit(new Runnable() {
11013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        /** {@inheritDoc} */
11023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        @Override
11033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        public void run() {
11043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            status.serviceTypeAdded(event);
11053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        }
11063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    });
11073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
11083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
11093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
11103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (subtype.length() > 0) {
11113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            ServiceTypeEntry subtypes = _serviceTypes.get(loname);
11123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if ((subtypes != null) && (!subtypes.contains(subtype))) {
11133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                synchronized (subtypes) {
11143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    if (!subtypes.contains(subtype)) {
11153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        typeAdded = true;
11163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        subtypes.add(subtype);
11173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        final ServiceTypeListenerStatus[] list = _typeListeners.toArray(new ServiceTypeListenerStatus[_typeListeners.size()]);
11183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        final ServiceEvent event = new ServiceEventImpl(this, "_" + subtype + "._sub." + name, "", null);
11193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        for (final ServiceTypeListenerStatus status : list) {
11203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            _executor.submit(new Runnable() {
11213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                /** {@inheritDoc} */
11223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                @Override
11233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                public void run() {
11243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                    status.subTypeForServiceTypeAdded(event);
11253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                }
11263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            });
11273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        }
11283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    }
11293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
11303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
11313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
11323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return typeAdded;
11333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
11343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
11353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
11363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Generate a possibly unique name for a service using the information we have in the cache.
11373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
11383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @return returns true, if the name of the service info had to be changed.
11393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
11403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private boolean makeServiceNameUnique(ServiceInfoImpl info) {
11413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        final String originalQualifiedName = info.getKey();
11423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        final long now = System.currentTimeMillis();
11433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
11443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        boolean collision;
11453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        do {
11463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            collision = false;
11473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
11483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            // Check for collision in cache
11493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            for (DNSEntry dnsEntry : this.getCache().getDNSEntryList(info.getKey())) {
11503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (DNSRecordType.TYPE_SRV.equals(dnsEntry.getRecordType()) && !dnsEntry.isExpired(now)) {
11513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    final DNSRecord.Service s = (DNSRecord.Service) dnsEntry;
11523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    if (s.getPort() != info.getPort() || !s.getServer().equals(_localHost.getName())) {
11533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        if (logger.isLoggable(Level.FINER)) {
11543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            logger.finer("makeServiceNameUnique() JmDNS.makeServiceNameUnique srv collision:" + dnsEntry + " s.server=" + s.getServer() + " " + _localHost.getName() + " equals:" + (s.getServer().equals(_localHost.getName())));
11553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        }
11563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        info.setName(incrementName(info.getName()));
11573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        collision = true;
11583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        break;
11593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    }
11603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
11613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
11623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
11633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            // Check for collision with other service infos published by JmDNS
11643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            final ServiceInfo selfService = _services.get(info.getKey());
11653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (selfService != null && selfService != info) {
11663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                info.setName(incrementName(info.getName()));
11673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                collision = true;
11683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
11693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
11703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        while (collision);
11713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
11723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return !(originalQualifiedName.equals(info.getKey()));
11733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
11743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
11753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    String incrementName(String name) {
11763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        String aName = name;
11773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        try {
11783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            final int l = aName.lastIndexOf('(');
11793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            final int r = aName.lastIndexOf(')');
11803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if ((l >= 0) && (l < r)) {
11813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                aName = aName.substring(0, l) + "(" + (Integer.parseInt(aName.substring(l + 1, r)) + 1) + ")";
11823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            } else {
11833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                aName += " (2)";
11843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
11853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        } catch (final NumberFormatException e) {
11863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            aName += " (2)";
11873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
11883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return aName;
11893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
11903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
11913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
11923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Add a listener for a question. The listener will receive updates of answers to the question as they arrive, or from the cache if they are already available.
11933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
11943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param listener
11953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *            DSN listener
11963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param question
11973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *            DNS query
11983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
11993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void addListener(DNSListener listener, DNSQuestion question) {
12003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        final long now = System.currentTimeMillis();
12013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
12023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // add the new listener
12033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        _listeners.add(listener);
12043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
12053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // report existing matched records
12063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
12073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (question != null) {
12083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            for (DNSEntry dnsEntry : this.getCache().getDNSEntryList(question.getName().toLowerCase())) {
12093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (question.answeredBy(dnsEntry) && !dnsEntry.isExpired(now)) {
12103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    listener.updateRecord(this.getCache(), now, dnsEntry);
12113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
12123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
12133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
12143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
12153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
12163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
12173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Remove a listener from all outstanding questions. The listener will no longer receive any updates.
12183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
12193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param listener
12203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *            DSN listener
12213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
12223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void removeListener(DNSListener listener) {
12233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        _listeners.remove(listener);
12243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
12253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
12263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
12273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Renew a service when the record become stale. If there is no service collector for the type this method does nothing.
12283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
12293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param record
12303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *            DNS record
12313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
12323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void renewServiceCollector(DNSRecord record) {
12333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        ServiceInfo info = record.getServiceInfo();
12343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (_serviceCollectors.containsKey(info.getType().toLowerCase())) {
12353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            // Create/start ServiceResolver
12363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this.startServiceResolver(info.getType());
12373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
12383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
12393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
12403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    // Remind: Method updateRecord should receive a better name.
12413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
12423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Notify all listeners that a record was updated.
12433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
12443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param now
12453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *            update date
12463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param rec
12473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *            DNS record
12483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param operation
12493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *            DNS cache operation
12503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
12513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void updateRecord(long now, DNSRecord rec, Operation operation) {
12523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // We do not want to block the entire DNS while we are updating the record for each listener (service info)
12533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        {
12543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            List<DNSListener> listenerList = null;
12553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            synchronized (_listeners) {
12563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                listenerList = new ArrayList<DNSListener>(_listeners);
12573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
12583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            for (DNSListener listener : listenerList) {
12593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                listener.updateRecord(this.getCache(), now, rec);
12603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
12613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
12623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (DNSRecordType.TYPE_PTR.equals(rec.getRecordType()))
12633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // if (DNSRecordType.TYPE_PTR.equals(rec.getRecordType()) || DNSRecordType.TYPE_SRV.equals(rec.getRecordType()))
12643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        {
12653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            ServiceEvent event = rec.getServiceEvent(this);
12663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if ((event.getInfo() == null) || !event.getInfo().hasData()) {
12673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                // We do not care about the subtype because the info is only used if complete and the subtype will then be included.
12683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                ServiceInfo info = this.getServiceInfoFromCache(event.getType(), event.getName(), "", false);
12693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (info.hasData()) {
12703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    event = new ServiceEventImpl(this, event.getType(), event.getName(), info);
12713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
12723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
12733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
12743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            List<ServiceListenerStatus> list = _serviceListeners.get(event.getType().toLowerCase());
12753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            final List<ServiceListenerStatus> serviceListenerList;
12763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (list != null) {
12773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                synchronized (list) {
12783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    serviceListenerList = new ArrayList<ServiceListenerStatus>(list);
12793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
12803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            } else {
12813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                serviceListenerList = Collections.emptyList();
12823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
12833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (logger.isLoggable(Level.FINEST)) {
12843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                logger.finest(this.getName() + ".updating record for event: " + event + " list " + serviceListenerList + " operation: " + operation);
12853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
12863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (!serviceListenerList.isEmpty()) {
12873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                final ServiceEvent localEvent = event;
12883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
12893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                switch (operation) {
12903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    case Add:
12913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        for (final ServiceListenerStatus listener : serviceListenerList) {
12923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            if (listener.isSynchronous()) {
12933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                listener.serviceAdded(localEvent);
12943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            } else {
12953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                _executor.submit(new Runnable() {
12963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                    /** {@inheritDoc} */
12973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                    @Override
12983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                    public void run() {
12993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                        listener.serviceAdded(localEvent);
13003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                    }
13013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                });
13023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            }
13033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        }
13043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        break;
13053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    case Remove:
13063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        for (final ServiceListenerStatus listener : serviceListenerList) {
13073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            if (listener.isSynchronous()) {
13083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                listener.serviceRemoved(localEvent);
13093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            } else {
13103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                _executor.submit(new Runnable() {
13113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                    /** {@inheritDoc} */
13123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                    @Override
13133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                    public void run() {
13143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                        listener.serviceRemoved(localEvent);
13153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                    }
13163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                });
13173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            }
13183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        }
13193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        break;
13203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    default:
13213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        break;
13223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
13233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
13243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
13253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
13263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
13273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    void handleRecord(DNSRecord record, long now) {
13283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        DNSRecord newRecord = record;
13293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
13303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        Operation cacheOperation = Operation.Noop;
13313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        final boolean expired = newRecord.isExpired(now);
13323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (logger.isLoggable(Level.FINE)) {
13333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            logger.fine(this.getName() + " handle response: " + newRecord);
13343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
13353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
13363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // update the cache
13373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (!newRecord.isServicesDiscoveryMetaQuery() && !newRecord.isDomainDiscoveryQuery()) {
13383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            final boolean unique = newRecord.isUnique();
13393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            final DNSRecord cachedRecord = (DNSRecord) this.getCache().getDNSEntry(newRecord);
13403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (logger.isLoggable(Level.FINE)) {
13413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                logger.fine(this.getName() + " handle response cached record: " + cachedRecord);
13423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
13433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (unique) {
13443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                for (DNSEntry entry : this.getCache().getDNSEntryList(newRecord.getKey())) {
13453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    if (newRecord.getRecordType().equals(entry.getRecordType()) && newRecord.getRecordClass().equals(entry.getRecordClass()) && (entry != cachedRecord)) {
13463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        ((DNSRecord) entry).setWillExpireSoon(now);
13473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    }
13483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
13493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
13503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (cachedRecord != null) {
13513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (expired) {
13523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    // if the record has a 0 ttl that means we have a cancel record we need to delay the removal by 1s
13533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    if (newRecord.getTTL() == 0) {
13543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        cacheOperation = Operation.Noop;
13553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        cachedRecord.setWillExpireSoon(now);
13563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        // the actual record will be disposed of by the record reaper.
13573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    } else {
13583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        cacheOperation = Operation.Remove;
13593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        this.getCache().removeDNSEntry(cachedRecord);
13603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    }
13613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                } else {
13623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    // If the record content has changed we need to inform our listeners.
13633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    if (!newRecord.sameValue(cachedRecord) || (!newRecord.sameSubtype(cachedRecord) && (newRecord.getSubtype().length() > 0))) {
13643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        if (newRecord.isSingleValued()) {
13653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            cacheOperation = Operation.Update;
13663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            this.getCache().replaceDNSEntry(newRecord, cachedRecord);
13673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        } else {
13683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            // Address record can have more than one value on multi-homed machines
13693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            cacheOperation = Operation.Add;
13703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            this.getCache().addDNSEntry(newRecord);
13713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        }
13723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    } else {
13733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        cachedRecord.resetTTL(newRecord);
13743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        newRecord = cachedRecord;
13753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    }
13763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
13773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            } else {
13783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (!expired) {
13793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    cacheOperation = Operation.Add;
13803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    this.getCache().addDNSEntry(newRecord);
13813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
13823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
13833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
13843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
13853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // Register new service types
13863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (newRecord.getRecordType() == DNSRecordType.TYPE_PTR) {
13873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            // handle DNSConstants.DNS_META_QUERY records
13883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            boolean typeAdded = false;
13893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (newRecord.isServicesDiscoveryMetaQuery()) {
13903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                // The service names are in the alias.
13913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (!expired) {
13923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    typeAdded = this.registerServiceType(((DNSRecord.Pointer) newRecord).getAlias());
13933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
13943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                return;
13953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
13963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            typeAdded |= this.registerServiceType(newRecord.getName());
13973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (typeAdded && (cacheOperation == Operation.Noop)) {
13983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                cacheOperation = Operation.RegisterServiceType;
13993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
14003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
14013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
14023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // notify the listeners
14033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (cacheOperation != Operation.Noop) {
14043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this.updateRecord(now, newRecord, cacheOperation);
14053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
14063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
14073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
14083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
14093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
14103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Handle an incoming response. Cache answers, and pass them on to the appropriate questions.
14113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
14123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @exception IOException
14133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
14143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    void handleResponse(DNSIncoming msg) throws IOException {
14153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        final long now = System.currentTimeMillis();
14163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
14173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        boolean hostConflictDetected = false;
14183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        boolean serviceConflictDetected = false;
14193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
14203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        for (DNSRecord newRecord : msg.getAllAnswers()) {
14213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this.handleRecord(newRecord, now);
14223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
14233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (DNSRecordType.TYPE_A.equals(newRecord.getRecordType()) || DNSRecordType.TYPE_AAAA.equals(newRecord.getRecordType())) {
14243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                hostConflictDetected |= newRecord.handleResponse(this);
14253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            } else {
14263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                serviceConflictDetected |= newRecord.handleResponse(this);
14273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
14283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
14293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
14303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
14313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (hostConflictDetected || serviceConflictDetected) {
14323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this.startProber();
14333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
14343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
14353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
14363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
14373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Handle an incoming query. See if we can answer any part of it given our service infos.
14383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
14393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param in
14403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param addr
14413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param port
14423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @exception IOException
14433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
14443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    void handleQuery(DNSIncoming in, InetAddress addr, int port) throws IOException {
14453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (logger.isLoggable(Level.FINE)) {
14463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            logger.fine(this.getName() + ".handle query: " + in);
14473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
14483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // Track known answers
14493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        boolean conflictDetected = false;
14503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        final long expirationTime = System.currentTimeMillis() + DNSConstants.KNOWN_ANSWER_TTL;
14513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        for (DNSRecord answer : in.getAllAnswers()) {
14523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            conflictDetected |= answer.handleQuery(this, expirationTime);
14533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
14543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
14553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.ioLock();
14563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        try {
14573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
14583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (_plannedAnswer != null) {
14593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                _plannedAnswer.append(in);
14603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            } else {
14613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                DNSIncoming plannedAnswer = in.clone();
14623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (in.isTruncated()) {
14633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    _plannedAnswer = plannedAnswer;
14643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
14653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                this.startResponder(plannedAnswer, port);
14663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
14673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
14683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        } finally {
14693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this.ioUnlock();
14703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
14713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
14723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        final long now = System.currentTimeMillis();
14733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        for (DNSRecord answer : in.getAnswers()) {
14743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this.handleRecord(answer, now);
14753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
14763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
14773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (conflictDetected) {
14783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this.startProber();
14793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
14803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
14813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
14823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void respondToQuery(DNSIncoming in) {
14833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.ioLock();
14843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        try {
14853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (_plannedAnswer == in) {
14863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                _plannedAnswer = null;
14873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
14883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        } finally {
14893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this.ioUnlock();
14903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
14913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
14923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
14933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
14943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Add an answer to a question. Deal with the case when the outgoing packet overflows
14953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
14963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param in
14973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param addr
14983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param port
14993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param out
15003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param rec
15013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @return outgoing answer
15023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @exception IOException
15033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
15043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public DNSOutgoing addAnswer(DNSIncoming in, InetAddress addr, int port, DNSOutgoing out, DNSRecord rec) throws IOException {
15053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        DNSOutgoing newOut = out;
15063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (newOut == null) {
15073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            newOut = new DNSOutgoing(DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA, false, in.getSenderUDPPayload());
15083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
15093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        try {
15103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            newOut.addAnswer(in, rec);
15113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        } catch (final IOException e) {
15123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            newOut.setFlags(newOut.getFlags() | DNSConstants.FLAGS_TC);
15133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            newOut.setId(in.getId());
15143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            send(newOut);
15153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
15163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            newOut = new DNSOutgoing(DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA, false, in.getSenderUDPPayload());
15173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            newOut.addAnswer(in, rec);
15183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
15193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return newOut;
15203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
15213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
15223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
15233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Send an outgoing multicast DNS message.
15243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
15253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param out
15263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @exception IOException
15273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
15283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void send(DNSOutgoing out) throws IOException {
15293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (!out.isEmpty()) {
15303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            byte[] message = out.data();
15313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            final DatagramPacket packet = new DatagramPacket(message, message.length, _group, DNSConstants.MDNS_PORT);
15323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
15333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (logger.isLoggable(Level.FINEST)) {
15343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                try {
15353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    final DNSIncoming msg = new DNSIncoming(packet);
15363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    if (logger.isLoggable(Level.FINEST)) {
15373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        logger.finest("send(" + this.getName() + ") JmDNS out:" + msg.print(true));
15383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    }
15393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                } catch (final IOException e) {
15403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    logger.throwing(getClass().toString(), "send(" + this.getName() + ") - JmDNS can not parse what it sends!!!", e);
15413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
15423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
15433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            final MulticastSocket ms = _socket;
15443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (ms != null && !ms.isClosed()) {
15453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                ms.send(packet);
15463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
15473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
15483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
15493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
15503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /*
15513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * (non-Javadoc)
15523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @see javax.jmdns.impl.DNSTaskStarter#purgeTimer()
15533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
15543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
15553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void purgeTimer() {
15563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        DNSTaskStarter.Factory.getInstance().getStarter(this.getDns()).purgeTimer();
15573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
15583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
15593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /*
15603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * (non-Javadoc)
15613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @see javax.jmdns.impl.DNSTaskStarter#purgeStateTimer()
15623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
15633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
15643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void purgeStateTimer() {
15653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        DNSTaskStarter.Factory.getInstance().getStarter(this.getDns()).purgeStateTimer();
15663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
15673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
15683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /*
15693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * (non-Javadoc)
15703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @see javax.jmdns.impl.DNSTaskStarter#cancelTimer()
15713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
15723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
15733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void cancelTimer() {
15743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        DNSTaskStarter.Factory.getInstance().getStarter(this.getDns()).cancelTimer();
15753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
15763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
15773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /*
15783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * (non-Javadoc)
15793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @see javax.jmdns.impl.DNSTaskStarter#cancelStateTimer()
15803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
15813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
15823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void cancelStateTimer() {
15833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        DNSTaskStarter.Factory.getInstance().getStarter(this.getDns()).cancelStateTimer();
15843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
15853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
15863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /*
15873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * (non-Javadoc)
15883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @see javax.jmdns.impl.DNSTaskStarter#startProber()
15893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
15903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
15913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void startProber() {
15923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        DNSTaskStarter.Factory.getInstance().getStarter(this.getDns()).startProber();
15933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
15943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
15953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /*
15963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * (non-Javadoc)
15973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @see javax.jmdns.impl.DNSTaskStarter#startAnnouncer()
15983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
15993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
16003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void startAnnouncer() {
16013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        DNSTaskStarter.Factory.getInstance().getStarter(this.getDns()).startAnnouncer();
16023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
16033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
16043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /*
16053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * (non-Javadoc)
16063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @see javax.jmdns.impl.DNSTaskStarter#startRenewer()
16073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
16083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
16093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void startRenewer() {
16103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        DNSTaskStarter.Factory.getInstance().getStarter(this.getDns()).startRenewer();
16113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
16123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
16133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /*
16143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * (non-Javadoc)
16153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @see javax.jmdns.impl.DNSTaskStarter#startCanceler()
16163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
16173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
16183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void startCanceler() {
16193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        DNSTaskStarter.Factory.getInstance().getStarter(this.getDns()).startCanceler();
16203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
16213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
16223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /*
16233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * (non-Javadoc)
16243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @see javax.jmdns.impl.DNSTaskStarter#startReaper()
16253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
16263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
16273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void startReaper() {
16283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        DNSTaskStarter.Factory.getInstance().getStarter(this.getDns()).startReaper();
16293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
16303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
16313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /*
16323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * (non-Javadoc)
16333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @see javax.jmdns.impl.DNSTaskStarter#startServiceInfoResolver(javax.jmdns.impl.ServiceInfoImpl)
16343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
16353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
16363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void startServiceInfoResolver(ServiceInfoImpl info) {
16373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        DNSTaskStarter.Factory.getInstance().getStarter(this.getDns()).startServiceInfoResolver(info);
16383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
16393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
16403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /*
16413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * (non-Javadoc)
16423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @see javax.jmdns.impl.DNSTaskStarter#startTypeResolver()
16433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
16443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
16453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void startTypeResolver() {
16463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        DNSTaskStarter.Factory.getInstance().getStarter(this.getDns()).startTypeResolver();
16473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
16483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
16493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /*
16503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * (non-Javadoc)
16513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @see javax.jmdns.impl.DNSTaskStarter#startServiceResolver(java.lang.String)
16523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
16533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
16543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void startServiceResolver(String type) {
16553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        DNSTaskStarter.Factory.getInstance().getStarter(this.getDns()).startServiceResolver(type);
16563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
16573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
16583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /*
16593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * (non-Javadoc)
16603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @see javax.jmdns.impl.DNSTaskStarter#startResponder(javax.jmdns.impl.DNSIncoming, int)
16613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
16623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
16633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void startResponder(DNSIncoming in, int port) {
16643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        DNSTaskStarter.Factory.getInstance().getStarter(this.getDns()).startResponder(in, port);
16653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
16663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
16673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    // REMIND: Why is this not an anonymous inner class?
16683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
16693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Shutdown operations.
16703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
16713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    protected class Shutdown implements Runnable {
16723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        /** {@inheritDoc} */
16733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        @Override
16743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        public void run() {
16753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            try {
16763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                _shutdown = null;
16773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                close();
16783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            } catch (Throwable exception) {
16793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                System.err.println("Error while shuting down. " + exception);
16803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
16813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
16823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
16833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
16843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private final Object _recoverLock = new Object();
16853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
16863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
16873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Recover jmdns when there is an error.
16883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
16893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void recover() {
16903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        logger.finer(this.getName() + "recover()");
16913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // We have an IO error so lets try to recover if anything happens lets close it.
16923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // This should cover the case of the IP address changing under our feet
16933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (this.isClosing() || this.isClosed() || this.isCanceling() || this.isCanceled()) {
16943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            return;
16953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
16963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
16973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // We need some definite lock here as we may have multiple timer running in the same thread that will not be stopped by the reentrant lock
16983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // in the state object. This is only a problem in this case as we are going to execute in seperate thread so that the timer can clear.
16993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        synchronized (_recoverLock) {
17003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            // Stop JmDNS
17013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            // This protects against recursive calls
17023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (this.cancelState()) {
17033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                logger.finer(this.getName() + "recover() thread " + Thread.currentThread().getName());
17043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                Thread recover = new Thread(this.getName() + ".recover()") {
17053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    /**
17063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                     * {@inheritDoc}
17073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                     */
17083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    @Override
17093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    public void run() {
17103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        __recover();
17113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    }
17123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                };
17133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                recover.start();
17143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
17153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
17163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
17173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
17183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    void __recover() {
17193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // Synchronize only if we are not already in process to prevent dead locks
17203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        //
17213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (logger.isLoggable(Level.FINER)) {
17223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            logger.finer(this.getName() + "recover() Cleanning up");
17233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
17243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
17253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        logger.warning("RECOVERING");
17263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // Purge the timer
17273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.purgeTimer();
17283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
17293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // We need to keep a copy for reregistration
17303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        final Collection<ServiceInfo> oldServiceInfos = new ArrayList<ServiceInfo>(getServices().values());
17313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
17323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // Cancel all services
17333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.unregisterAllServices();
17343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.disposeServiceCollectors();
17353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
17363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.waitForCanceled(DNSConstants.CLOSE_TIMEOUT);
17373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
17383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // Purge the canceler timer
17393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.purgeStateTimer();
17403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
17413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        //
17423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // close multicast socket
17433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.closeMulticastSocket();
17443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
17453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        //
17463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.getCache().clear();
17473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (logger.isLoggable(Level.FINER)) {
17483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            logger.finer(this.getName() + "recover() All is clean");
17493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
17503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
17513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (this.isCanceled()) {
17523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            //
17533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            // All is clear now start the services
17543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            //
17553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            for (ServiceInfo info : oldServiceInfos) {
17563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                ((ServiceInfoImpl) info).recoverState();
17573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
17583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this.recoverState();
17593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
17603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            try {
17613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                this.openMulticastSocket(this.getLocalHost());
17623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                this.start(oldServiceInfos);
17633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            } catch (final Exception exception) {
17643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                logger.log(Level.WARNING, this.getName() + "recover() Start services exception ", exception);
17653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
17663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            logger.log(Level.WARNING, this.getName() + "recover() We are back!");
17673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        } else {
17683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            // We have a problem. We could not clear the state.
17693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            logger.log(Level.WARNING, this.getName() + "recover() Could not recover we are Down!");
17703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (this.getDelegate() != null) {
17713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                this.getDelegate().cannotRecoverFromIOError(this.getDns(), oldServiceInfos);
17723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
17733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
17743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
17753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
17763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
17773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void cleanCache() {
17783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        long now = System.currentTimeMillis();
17793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        for (DNSEntry entry : this.getCache().allValues()) {
17803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            try {
17813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                DNSRecord record = (DNSRecord) entry;
17823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (record.isExpired(now)) {
17833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    this.updateRecord(now, record, Operation.Remove);
17843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    this.getCache().removeDNSEntry(record);
17853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                } else if (record.isStale(now)) {
17863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    // we should query for the record we care about i.e. those in the service collectors
17873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    this.renewServiceCollector(record);
17883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
17893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            } catch (Exception exception) {
17903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                logger.log(Level.SEVERE, this.getName() + ".Error while reaping records: " + entry, exception);
17913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                logger.severe(this.toString());
17923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
17933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
17943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
17953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
17963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
17973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
17983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
17993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
18003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void close() {
18013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (this.isClosing()) {
18023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            return;
18033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
18043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
18053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (logger.isLoggable(Level.FINER)) {
18063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            logger.finer("Cancelling JmDNS: " + this);
18073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
18083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // Stop JmDNS
18093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // This protects against recursive calls
18103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (this.closeState()) {
18113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            // We got the tie break now clean up
18123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
18133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            // Stop the timer
18143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            logger.finer("Canceling the timer");
18153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this.cancelTimer();
18163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
18173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            // Cancel all services
18183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this.unregisterAllServices();
18193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this.disposeServiceCollectors();
18203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
18213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (logger.isLoggable(Level.FINER)) {
18223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                logger.finer("Wait for JmDNS cancel: " + this);
18233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
18243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this.waitForCanceled(DNSConstants.CLOSE_TIMEOUT);
18253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
18263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            // Stop the canceler timer
18273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            logger.finer("Canceling the state timer");
18283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this.cancelStateTimer();
18293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
18303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            // Stop the executor
18313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            _executor.shutdown();
18323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
18333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            // close socket
18343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this.closeMulticastSocket();
18353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
18363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            // remove the shutdown hook
18373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (_shutdown != null) {
18383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                Runtime.getRuntime().removeShutdownHook(_shutdown);
18393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
18403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
18413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (logger.isLoggable(Level.FINER)) {
18423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                logger.finer("JmDNS closed.");
18433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
18443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
18453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        advanceState(null);
18463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
18473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
18483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
18493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
18503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
18513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
18523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Deprecated
18533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void printServices() {
18543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        System.err.println(toString());
18553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
18563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
18573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
18583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
18593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
18603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
18613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public String toString() {
18623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        final StringBuilder aLog = new StringBuilder(2048);
18633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        aLog.append("\t---- Local Host -----");
18643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        aLog.append("\n\t");
18653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        aLog.append(_localHost);
18663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        aLog.append("\n\t---- Services -----");
18673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        for (String key : _services.keySet()) {
18683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            aLog.append("\n\t\tService: ");
18693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            aLog.append(key);
18703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            aLog.append(": ");
18713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            aLog.append(_services.get(key));
18723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
18733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        aLog.append("\n");
18743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        aLog.append("\t---- Types ----");
18753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        for (String key : _serviceTypes.keySet()) {
18763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            ServiceTypeEntry subtypes = _serviceTypes.get(key);
18773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            aLog.append("\n\t\tType: ");
18783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            aLog.append(subtypes.getType());
18793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            aLog.append(": ");
18803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            aLog.append(subtypes.isEmpty() ? "no subtypes" : subtypes);
18813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
18823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        aLog.append("\n");
18833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        aLog.append(_cache.toString());
18843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        aLog.append("\n");
18853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        aLog.append("\t---- Service Collectors ----");
18863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        for (String key : _serviceCollectors.keySet()) {
18873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            aLog.append("\n\t\tService Collector: ");
18883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            aLog.append(key);
18893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            aLog.append(": ");
18903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            aLog.append(_serviceCollectors.get(key));
18913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
18923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        aLog.append("\n");
18933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        aLog.append("\t---- Service Listeners ----");
18943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        for (String key : _serviceListeners.keySet()) {
18953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            aLog.append("\n\t\tService Listener: ");
18963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            aLog.append(key);
18973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            aLog.append(": ");
18983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            aLog.append(_serviceListeners.get(key));
18993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
19003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return aLog.toString();
19013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
19023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
19033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
19043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
19053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
19063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
19073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public ServiceInfo[] list(String type) {
19083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return this.list(type, DNSConstants.SERVICE_INFO_TIMEOUT);
19093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
19103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
19113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
19123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
19133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
19143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
19153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public ServiceInfo[] list(String type, long timeout) {
19163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.cleanCache();
19173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // Implementation note: The first time a list for a given type is
19183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // requested, a ServiceCollector is created which collects service
19193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // infos. This greatly speeds up the performance of subsequent calls
19203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // to this method. The caveats are, that 1) the first call to this
19213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // method for a given type is slow, and 2) we spawn a ServiceCollector
19223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // instance for each service type which increases network traffic a
19233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // little.
19243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
19253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        String loType = type.toLowerCase();
19263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
19273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        boolean newCollectorCreated = false;
19283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (this.isCanceling() || this.isCanceled()) {
19293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            return new ServiceInfo[0];
19303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
19313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
19323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        ServiceCollector collector = _serviceCollectors.get(loType);
19333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (collector == null) {
19343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            newCollectorCreated = _serviceCollectors.putIfAbsent(loType, new ServiceCollector(type)) == null;
19353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            collector = _serviceCollectors.get(loType);
19363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (newCollectorCreated) {
19373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                this.addServiceListener(type, collector, ListenerStatus.SYNCHONEOUS);
19383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
19393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
19403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (logger.isLoggable(Level.FINER)) {
19413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            logger.finer(this.getName() + ".collector: " + collector);
19423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
19433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // At this stage the collector should never be null but it keeps findbugs happy.
19443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return (collector != null ? collector.list(timeout) : new ServiceInfo[0]);
19453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
19463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
19473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
19483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
19493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
19503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
19513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public Map<String, ServiceInfo[]> listBySubtype(String type) {
19523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return this.listBySubtype(type, DNSConstants.SERVICE_INFO_TIMEOUT);
19533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
19543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
19553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
19563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * {@inheritDoc}
19573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
19583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
19593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public Map<String, ServiceInfo[]> listBySubtype(String type, long timeout) {
19603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        Map<String, List<ServiceInfo>> map = new HashMap<String, List<ServiceInfo>>(5);
19613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        for (ServiceInfo info : this.list(type, timeout)) {
19623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            String subtype = info.getSubtype().toLowerCase();
19633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (!map.containsKey(subtype)) {
19643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                map.put(subtype, new ArrayList<ServiceInfo>(10));
19653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
19663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            map.get(subtype).add(info);
19673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
19683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
19693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        Map<String, ServiceInfo[]> result = new HashMap<String, ServiceInfo[]>(map.size());
19703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        for (String subtype : map.keySet()) {
19713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            List<ServiceInfo> infoForSubType = map.get(subtype);
19723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            result.put(subtype, infoForSubType.toArray(new ServiceInfo[infoForSubType.size()]));
19733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
19743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
19753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return result;
19763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
19773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
19783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
19793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * This method disposes all ServiceCollector instances which have been created by calls to method <code>list(type)</code>.
19803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
19813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @see #list
19823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
19833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private void disposeServiceCollectors() {
19843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (logger.isLoggable(Level.FINER)) {
19853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            logger.finer("disposeServiceCollectors()");
19863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
19873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        for (String type : _serviceCollectors.keySet()) {
19883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            ServiceCollector collector = _serviceCollectors.get(type);
19893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (collector != null) {
19903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                this.removeServiceListener(type, collector);
19913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                _serviceCollectors.remove(type, collector);
19923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
19933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
19943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
19953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
19963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
19973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Instances of ServiceCollector are used internally to speed up the performance of method <code>list(type)</code>.
19983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
19993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @see #list
20003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
20013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private static class ServiceCollector implements ServiceListener {
20023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // private static Logger logger = Logger.getLogger(ServiceCollector.class.getName());
20033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
20043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        /**
20053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * A set of collected service instance names.
20063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         */
20073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        private final ConcurrentMap<String, ServiceInfo>  _infos;
20083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
20093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        /**
20103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * A set of collected service event waiting to be resolved.
20113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         */
20123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        private final ConcurrentMap<String, ServiceEvent> _events;
20133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
20143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        /**
20153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * This is the type we are listening for (only used for debugging).
20163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         */
20173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        private final String                              _type;
20183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
20193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        /**
20203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * This is used to force a wait on the first invocation of list.
20213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         */
20223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        private volatile boolean                          _needToWaitForInfos;
20233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
20243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        public ServiceCollector(String type) {
20253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            super();
20263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            _infos = new ConcurrentHashMap<String, ServiceInfo>();
20273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            _events = new ConcurrentHashMap<String, ServiceEvent>();
20283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            _type = type;
20293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            _needToWaitForInfos = true;
20303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
20313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
20323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        /**
20333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * A service has been added.
20343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         *
20353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * @param event
20363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         *            service event
20373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         */
20383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        @Override
20393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        public void serviceAdded(ServiceEvent event) {
20403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            synchronized (this) {
20413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                ServiceInfo info = event.getInfo();
20423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if ((info != null) && (info.hasData())) {
20433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    _infos.put(event.getName(), info);
20443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                } else {
20453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    String subtype = (info != null ? info.getSubtype() : "");
20463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    info = ((JmDNSImpl) event.getDNS()).resolveServiceInfo(event.getType(), event.getName(), subtype, true);
20473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    if (info != null) {
20483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        _infos.put(event.getName(), info);
20493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    } else {
20503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        _events.put(event.getName(), event);
20513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    }
20523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
20533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
20543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
20553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
20563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        /**
20573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * A service has been removed.
20583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         *
20593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * @param event
20603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         *            service event
20613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         */
20623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        @Override
20633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        public void serviceRemoved(ServiceEvent event) {
20643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            synchronized (this) {
20653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                _infos.remove(event.getName());
20663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                _events.remove(event.getName());
20673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
20683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
20693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
20703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        /**
20713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * A service has been resolved. Its details are now available in the ServiceInfo record.
20723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         *
20733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * @param event
20743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         *            service event
20753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         */
20763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        @Override
20773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        public void serviceResolved(ServiceEvent event) {
20783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            synchronized (this) {
20793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                _infos.put(event.getName(), event.getInfo());
20803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                _events.remove(event.getName());
20813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
20823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
20833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
20843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        /**
20853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * Returns an array of all service infos which have been collected by this ServiceCollector.
20863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         *
20873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * @param timeout
20883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         *            timeout if the info list is empty.
20893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * @return Service Info array
20903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         */
20913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        public ServiceInfo[] list(long timeout) {
20923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (_infos.isEmpty() || !_events.isEmpty() || _needToWaitForInfos) {
20933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                long loops = (timeout / 200L);
20943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (loops < 1) {
20953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    loops = 1;
20963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
20973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                for (int i = 0; i < loops; i++) {
20983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    try {
20993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        Thread.sleep(200);
21003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    } catch (final InterruptedException e) {
21013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        /* Stub */
21023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    }
21033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    if (_events.isEmpty() && !_infos.isEmpty() && !_needToWaitForInfos) {
21043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        break;
21053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    }
21063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
21073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
21083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            _needToWaitForInfos = false;
21093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            return _infos.values().toArray(new ServiceInfo[_infos.size()]);
21103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
21113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
21123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        /**
21133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * {@inheritDoc}
21143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         */
21153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        @Override
21163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        public String toString() {
21173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            final StringBuffer aLog = new StringBuffer();
21183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            aLog.append("\n\tType: ");
21193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            aLog.append(_type);
21203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (_infos.isEmpty()) {
21213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                aLog.append("\n\tNo services collected.");
21223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            } else {
21233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                aLog.append("\n\tServices");
21243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                for (String key : _infos.keySet()) {
21253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    aLog.append("\n\t\tService: ");
21263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    aLog.append(key);
21273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    aLog.append(": ");
21283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    aLog.append(_infos.get(key));
21293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
21303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
21313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (_events.isEmpty()) {
21323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                aLog.append("\n\tNo event queued.");
21333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            } else {
21343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                aLog.append("\n\tEvents");
21353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                for (String key : _events.keySet()) {
21363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    aLog.append("\n\t\tEvent: ");
21373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    aLog.append(key);
21383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    aLog.append(": ");
21393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    aLog.append(_events.get(key));
21403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
21413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
21423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            return aLog.toString();
21433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
21443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
21453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
21463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    static String toUnqualifiedName(String type, String qualifiedName) {
21473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        String loType = type.toLowerCase();
21483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        String loQualifiedName = qualifiedName.toLowerCase();
21493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (loQualifiedName.endsWith(loType) && !(loQualifiedName.equals(loType))) {
21503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            return qualifiedName.substring(0, qualifiedName.length() - type.length() - 1);
21513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
21523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return qualifiedName;
21533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
21543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
21553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public Map<String, ServiceInfo> getServices() {
21563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return _services;
21573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
21583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
21593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void setLastThrottleIncrement(long lastThrottleIncrement) {
21603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this._lastThrottleIncrement = lastThrottleIncrement;
21613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
21623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
21633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public long getLastThrottleIncrement() {
21643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return _lastThrottleIncrement;
21653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
21663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
21673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void setThrottle(int throttle) {
21683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this._throttle = throttle;
21693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
21703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
21713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public int getThrottle() {
21723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return _throttle;
21733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
21743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
21753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public static Random getRandom() {
21763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return _random;
21773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
21783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
21793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void ioLock() {
21803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        _ioLock.lock();
21813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
21823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
21833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void ioUnlock() {
21843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        _ioLock.unlock();
21853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
21863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
21873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void setPlannedAnswer(DNSIncoming plannedAnswer) {
21883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this._plannedAnswer = plannedAnswer;
21893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
21903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
21913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public DNSIncoming getPlannedAnswer() {
21923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return _plannedAnswer;
21933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
21943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
21953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    void setLocalHost(HostInfo localHost) {
21963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this._localHost = localHost;
21973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
21983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
21993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public Map<String, ServiceTypeEntry> getServiceTypes() {
22003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return _serviceTypes;
22013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
22023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
22033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public MulticastSocket getSocket() {
22043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return _socket;
22053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
22063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
22073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public InetAddress getGroup() {
22083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return _group;
22093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
22103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
22113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
22123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public Delegate getDelegate() {
22133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return this._delegate;
22143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
22153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
22163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
22173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public Delegate setDelegate(Delegate delegate) {
22183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        Delegate previous = this._delegate;
22193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this._delegate = delegate;
22203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return previous;
22213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
22223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
22233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman}
2224