blob: af2ed634491bf034c98d55adc34b7aca8d577758 [file] [log] [blame]
//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();
}
}
}
}