//Copyright 2003-2005 Arthur van Hoff, Rick Blair //Licensed under Apache License version 2.0 //Original license LGPL package javax.jmdns.impl.tasks; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Timer; import java.util.TimerTask; //import java.util.logging.Level; //import java.util.logging.Logger; import javax.jmdns.impl.DNSConstants; import javax.jmdns.impl.DNSOutgoing; import javax.jmdns.impl.DNSQuestion; import javax.jmdns.impl.DNSRecord; import javax.jmdns.impl.DNSState; import javax.jmdns.impl.JmDNSImpl; import javax.jmdns.impl.ServiceInfoImpl; /** * The Prober sends three consecutive probes for all service infos * that needs probing as well as for the host name. * The state of each service info of the host name is advanced, when a probe has * been sent for it. * When the prober has run three times, it launches an Announcer. * <p/> * If a conflict during probes occurs, the affected service infos (and affected * host name) are taken away from the prober. This eventually causes the prober * tho cancel itself. */ public class Prober extends TimerTask { // static Logger logger = Logger.getLogger(Prober.class.getName()); /** * */ private final JmDNSImpl jmDNSImpl; /** * The state of the prober. */ DNSState taskState = DNSState.PROBING_1; public Prober(JmDNSImpl jmDNSImpl) { this.jmDNSImpl = jmDNSImpl; // Associate the host name to this, if it needs probing if (this.jmDNSImpl.getState() == DNSState.PROBING_1) { this.jmDNSImpl.setTask(this); } // Associate services to this, if they need probing synchronized (this.jmDNSImpl) { for (Iterator iterator = this.jmDNSImpl.getServices().values().iterator(); iterator.hasNext();) { ServiceInfoImpl info = (ServiceInfoImpl) iterator.next(); if (info.getState() == DNSState.PROBING_1) { info.setTask(this); } } } } public void start(Timer timer) { long now = System.currentTimeMillis(); if (now - this.jmDNSImpl.getLastThrottleIncrement() < DNSConstants.PROBE_THROTTLE_COUNT_INTERVAL) { this.jmDNSImpl.setThrottle(this.jmDNSImpl.getThrottle() + 1); } else { this.jmDNSImpl.setThrottle(1); } this.jmDNSImpl.setLastThrottleIncrement(now); if (this.jmDNSImpl.getState() == DNSState.ANNOUNCED && this.jmDNSImpl.getThrottle() < DNSConstants.PROBE_THROTTLE_COUNT) { timer.schedule(this, JmDNSImpl.getRandom().nextInt(1 + DNSConstants.PROBE_WAIT_INTERVAL), DNSConstants.PROBE_WAIT_INTERVAL); } else { timer.schedule(this, DNSConstants.PROBE_CONFLICT_INTERVAL, DNSConstants.PROBE_CONFLICT_INTERVAL); } } public boolean cancel() { // Remove association from host name to this if (this.jmDNSImpl.getTask() == this) { this.jmDNSImpl.setTask(null); } // Remove associations from services to this synchronized (this.jmDNSImpl) { for (Iterator i = this.jmDNSImpl.getServices().values().iterator(); i.hasNext();) { ServiceInfoImpl info = (ServiceInfoImpl) i.next(); if (info.getTask() == this) { info.setTask(null); } } } return super.cancel(); } public void run() { synchronized (this.jmDNSImpl.getIoLock()) { DNSOutgoing out = null; try { // send probes for JmDNS itself if (this.jmDNSImpl.getState() == taskState && this.jmDNSImpl.getTask() == this) { if (out == null) { out = new DNSOutgoing(DNSConstants.FLAGS_QR_QUERY); } out.addQuestion(new DNSQuestion(this.jmDNSImpl.getLocalHost().getName(), DNSConstants.TYPE_ANY, DNSConstants.CLASS_IN)); this.jmDNSImpl.getLocalHost().addAddressRecords(out, true); this.jmDNSImpl.advanceState(); } // send probes for services // Defensively copy the services into a local list, // to prevent race conditions with methods registerService // and unregisterService. List list; synchronized (this.jmDNSImpl) { list = new LinkedList(this.jmDNSImpl.getServices().values()); } for (Iterator i = list.iterator(); i.hasNext();) { ServiceInfoImpl info = (ServiceInfoImpl) i.next(); synchronized (info) { if (info.getState() == taskState && info.getTask() == this) { info.advanceState(); // logger.fine("run() JmDNS probing " + info.getQualifiedName() + " state " + info.getState()); if (out == null) { out = new DNSOutgoing(DNSConstants.FLAGS_QR_QUERY); out.addQuestion(new DNSQuestion(info.getQualifiedName(), DNSConstants.TYPE_ANY, DNSConstants.CLASS_IN)); } // the "unique" flag should be not set here because these answers haven't been proven unique yet // this means the record will not exactly match the announcement record out.addAuthorativeAnswer(new DNSRecord.Service(info.getQualifiedName(), DNSConstants.TYPE_SRV, DNSConstants.CLASS_IN, DNSConstants.DNS_TTL, info.getPriority(), info.getWeight(), info.getPort(), this.jmDNSImpl.getLocalHost().getName())); } } } if (out != null) { // logger.finer("run() JmDNS probing #" + taskState); this.jmDNSImpl.send(out); } else { // If we have nothing to send, another timer taskState ahead // of us has done the job for us. We can cancel. cancel(); return; } } catch (Throwable e) { // logger.log(Level.WARNING, "run() exception ", e); this.jmDNSImpl.recover(); } taskState = taskState.advance(); if (!taskState.isProbing()) { cancel(); this.jmDNSImpl.startAnnouncer(); } } } }