summaryrefslogtreecommitdiff
path: root/src/javax/jmdns/impl/tasks
diff options
context:
space:
mode:
Diffstat (limited to 'src/javax/jmdns/impl/tasks')
-rw-r--r--src/javax/jmdns/impl/tasks/DNSTask.java223
-rw-r--r--src/javax/jmdns/impl/tasks/RecordReaper.java61
-rw-r--r--src/javax/jmdns/impl/tasks/Responder.java157
-rw-r--r--src/javax/jmdns/impl/tasks/package-info.java2
-rw-r--r--src/javax/jmdns/impl/tasks/resolver/DNSResolverTask.java116
-rw-r--r--src/javax/jmdns/impl/tasks/resolver/ServiceInfoResolver.java102
-rw-r--r--src/javax/jmdns/impl/tasks/resolver/ServiceResolver.java77
-rw-r--r--src/javax/jmdns/impl/tasks/resolver/TypeResolver.java75
-rw-r--r--src/javax/jmdns/impl/tasks/resolver/package-info.java2
-rw-r--r--src/javax/jmdns/impl/tasks/state/Announcer.java146
-rw-r--r--src/javax/jmdns/impl/tasks/state/Canceler.java143
-rw-r--r--src/javax/jmdns/impl/tasks/state/DNSStateTask.java188
-rw-r--r--src/javax/jmdns/impl/tasks/state/Prober.java161
-rw-r--r--src/javax/jmdns/impl/tasks/state/Renewer.java144
-rw-r--r--src/javax/jmdns/impl/tasks/state/package-info.java2
15 files changed, 1599 insertions, 0 deletions
diff --git a/src/javax/jmdns/impl/tasks/DNSTask.java b/src/javax/jmdns/impl/tasks/DNSTask.java
new file mode 100644
index 0000000..f7affd5
--- /dev/null
+++ b/src/javax/jmdns/impl/tasks/DNSTask.java
@@ -0,0 +1,223 @@
+// Licensed under Apache License version 2.0
+package javax.jmdns.impl.tasks;
+
+import java.io.IOException;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import javax.jmdns.impl.DNSIncoming;
+import javax.jmdns.impl.DNSOutgoing;
+import javax.jmdns.impl.DNSQuestion;
+import javax.jmdns.impl.DNSRecord;
+import javax.jmdns.impl.JmDNSImpl;
+import javax.jmdns.impl.constants.DNSConstants;
+
+/**
+ * This is the root class for all task scheduled by the timer in JmDNS.
+ *
+ * @author Pierre Frisch
+ */
+public abstract class DNSTask extends TimerTask {
+
+ /**
+ *
+ */
+ private final JmDNSImpl _jmDNSImpl;
+
+ /**
+ * @param jmDNSImpl
+ */
+ protected DNSTask(JmDNSImpl jmDNSImpl) {
+ super();
+ this._jmDNSImpl = jmDNSImpl;
+ }
+
+ /**
+ * Return the DNS associated with this task.
+ *
+ * @return associated DNS
+ */
+ public JmDNSImpl getDns() {
+ return _jmDNSImpl;
+ }
+
+ /**
+ * Start this task.
+ *
+ * @param timer
+ * task timer.
+ */
+ public abstract void start(Timer timer);
+
+ /**
+ * Return this task name.
+ *
+ * @return task name
+ */
+ public abstract String getName();
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return this.getName();
+ }
+
+ /**
+ * Add a question to the message.
+ *
+ * @param out
+ * outgoing message
+ * @param rec
+ * DNS question
+ * @return outgoing message for the next question
+ * @exception IOException
+ */
+ public DNSOutgoing addQuestion(DNSOutgoing out, DNSQuestion rec) throws IOException {
+ DNSOutgoing newOut = out;
+ try {
+ newOut.addQuestion(rec);
+ } catch (final IOException e) {
+ int flags = newOut.getFlags();
+ boolean multicast = newOut.isMulticast();
+ int maxUDPPayload = newOut.getMaxUDPPayload();
+ int id = newOut.getId();
+
+ newOut.setFlags(flags | DNSConstants.FLAGS_TC);
+ newOut.setId(id);
+ this._jmDNSImpl.send(newOut);
+
+ newOut = new DNSOutgoing(flags, multicast, maxUDPPayload);
+ newOut.addQuestion(rec);
+ }
+ return newOut;
+ }
+
+ /**
+ * Add an answer if it is not suppressed.
+ *
+ * @param out
+ * outgoing message
+ * @param in
+ * incoming request
+ * @param rec
+ * DNS record answer
+ * @return outgoing message for the next answer
+ * @exception IOException
+ */
+ public DNSOutgoing addAnswer(DNSOutgoing out, DNSIncoming in, DNSRecord rec) throws IOException {
+ DNSOutgoing newOut = out;
+ try {
+ newOut.addAnswer(in, rec);
+ } catch (final IOException e) {
+ int flags = newOut.getFlags();
+ boolean multicast = newOut.isMulticast();
+ int maxUDPPayload = newOut.getMaxUDPPayload();
+ int id = newOut.getId();
+
+ newOut.setFlags(flags | DNSConstants.FLAGS_TC);
+ newOut.setId(id);
+ this._jmDNSImpl.send(newOut);
+
+ newOut = new DNSOutgoing(flags, multicast, maxUDPPayload);
+ newOut.addAnswer(in, rec);
+ }
+ return newOut;
+ }
+
+ /**
+ * Add an answer to the message.
+ *
+ * @param out
+ * outgoing message
+ * @param rec
+ * DNS record answer
+ * @param now
+ * @return outgoing message for the next answer
+ * @exception IOException
+ */
+ public DNSOutgoing addAnswer(DNSOutgoing out, DNSRecord rec, long now) throws IOException {
+ DNSOutgoing newOut = out;
+ try {
+ newOut.addAnswer(rec, now);
+ } catch (final IOException e) {
+ int flags = newOut.getFlags();
+ boolean multicast = newOut.isMulticast();
+ int maxUDPPayload = newOut.getMaxUDPPayload();
+ int id = newOut.getId();
+
+ newOut.setFlags(flags | DNSConstants.FLAGS_TC);
+ newOut.setId(id);
+ this._jmDNSImpl.send(newOut);
+
+ newOut = new DNSOutgoing(flags, multicast, maxUDPPayload);
+ newOut.addAnswer(rec, now);
+ }
+ return newOut;
+ }
+
+ /**
+ * Add an authoritative answer to the message.
+ *
+ * @param out
+ * outgoing message
+ * @param rec
+ * DNS record answer
+ * @return outgoing message for the next answer
+ * @exception IOException
+ */
+ public DNSOutgoing addAuthoritativeAnswer(DNSOutgoing out, DNSRecord rec) throws IOException {
+ DNSOutgoing newOut = out;
+ try {
+ newOut.addAuthorativeAnswer(rec);
+ } catch (final IOException e) {
+ int flags = newOut.getFlags();
+ boolean multicast = newOut.isMulticast();
+ int maxUDPPayload = newOut.getMaxUDPPayload();
+ int id = newOut.getId();
+
+ newOut.setFlags(flags | DNSConstants.FLAGS_TC);
+ newOut.setId(id);
+ this._jmDNSImpl.send(newOut);
+
+ newOut = new DNSOutgoing(flags, multicast, maxUDPPayload);
+ newOut.addAuthorativeAnswer(rec);
+ }
+ return newOut;
+ }
+
+ /**
+ * Add an additional answer to the record. Omit if there is no room.
+ *
+ * @param out
+ * outgoing message
+ * @param in
+ * incoming request
+ * @param rec
+ * DNS record answer
+ * @return outgoing message for the next answer
+ * @exception IOException
+ */
+ public DNSOutgoing addAdditionalAnswer(DNSOutgoing out, DNSIncoming in, DNSRecord rec) throws IOException {
+ DNSOutgoing newOut = out;
+ try {
+ newOut.addAdditionalAnswer(in, rec);
+ } catch (final IOException e) {
+ int flags = newOut.getFlags();
+ boolean multicast = newOut.isMulticast();
+ int maxUDPPayload = newOut.getMaxUDPPayload();
+ int id = newOut.getId();
+
+ newOut.setFlags(flags | DNSConstants.FLAGS_TC);
+ newOut.setId(id);
+ this._jmDNSImpl.send(newOut);
+
+ newOut = new DNSOutgoing(flags, multicast, maxUDPPayload);
+ newOut.addAdditionalAnswer(in, rec);
+ }
+ return newOut;
+ }
+
+}
diff --git a/src/javax/jmdns/impl/tasks/RecordReaper.java b/src/javax/jmdns/impl/tasks/RecordReaper.java
new file mode 100644
index 0000000..75dd0ef
--- /dev/null
+++ b/src/javax/jmdns/impl/tasks/RecordReaper.java
@@ -0,0 +1,61 @@
+// 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.Timer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.jmdns.impl.JmDNSImpl;
+import javax.jmdns.impl.constants.DNSConstants;
+
+/**
+ * Periodically removes expired entries from the cache.
+ */
+public class RecordReaper extends DNSTask {
+ static Logger logger = Logger.getLogger(RecordReaper.class.getName());
+
+ /**
+ * @param jmDNSImpl
+ */
+ public RecordReaper(JmDNSImpl jmDNSImpl) {
+ super(jmDNSImpl);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.DNSTask#getName()
+ */
+ @Override
+ public String getName() {
+ return "RecordReaper(" + (this.getDns() != null ? this.getDns().getName() : "") + ")";
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.DNSTask#start(java.util.Timer)
+ */
+ @Override
+ public void start(Timer timer) {
+ if (!this.getDns().isCanceling() && !this.getDns().isCanceled()) {
+ timer.schedule(this, DNSConstants.RECORD_REAPER_INTERVAL, DNSConstants.RECORD_REAPER_INTERVAL);
+ }
+ }
+
+ @Override
+ public void run() {
+ if (this.getDns().isCanceling() || this.getDns().isCanceled()) {
+ return;
+ }
+ if (logger.isLoggable(Level.FINEST)) {
+ logger.finest(this.getName() + ".run() JmDNS reaping cache");
+ }
+
+ // Remove expired answers from the cache
+ // -------------------------------------
+ this.getDns().cleanCache();
+ }
+
+} \ No newline at end of file
diff --git a/src/javax/jmdns/impl/tasks/Responder.java b/src/javax/jmdns/impl/tasks/Responder.java
new file mode 100644
index 0000000..1fbbe75
--- /dev/null
+++ b/src/javax/jmdns/impl/tasks/Responder.java
@@ -0,0 +1,157 @@
+// 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.HashSet;
+import java.util.Set;
+import java.util.Timer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.jmdns.impl.DNSIncoming;
+import javax.jmdns.impl.DNSOutgoing;
+import javax.jmdns.impl.DNSQuestion;
+import javax.jmdns.impl.DNSRecord;
+import javax.jmdns.impl.JmDNSImpl;
+import javax.jmdns.impl.constants.DNSConstants;
+
+/**
+ * The Responder sends a single answer for the specified service infos and for the host name.
+ */
+public class Responder extends DNSTask {
+ static Logger logger = Logger.getLogger(Responder.class.getName());
+
+ /**
+ *
+ */
+ private final DNSIncoming _in;
+
+ /**
+ *
+ */
+ private final boolean _unicast;
+
+ public Responder(JmDNSImpl jmDNSImpl, DNSIncoming in, int port) {
+ super(jmDNSImpl);
+ this._in = in;
+ this._unicast = (port != DNSConstants.MDNS_PORT);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.DNSTask#getName()
+ */
+ @Override
+ public String getName() {
+ return "Responder(" + (this.getDns() != null ? this.getDns().getName() : "") + ")";
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return super.toString() + " incomming: " + _in;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.DNSTask#start(java.util.Timer)
+ */
+ @Override
+ public void start(Timer timer) {
+ // According to draft-cheshire-dnsext-multicastdns.txt chapter "7 Responding":
+ // We respond immediately if we know for sure, that we are the only one who can respond to the query.
+ // In all other cases, we respond within 20-120 ms.
+ //
+ // According to draft-cheshire-dnsext-multicastdns.txt chapter "6.2 Multi-Packet Known Answer Suppression":
+ // We respond after 20-120 ms if the query is truncated.
+
+ boolean iAmTheOnlyOne = true;
+ for (DNSQuestion question : _in.getQuestions()) {
+ if (logger.isLoggable(Level.FINEST)) {
+ logger.finest(this.getName() + "start() question=" + question);
+ }
+ iAmTheOnlyOne = question.iAmTheOnlyOne(this.getDns());
+ if (!iAmTheOnlyOne) {
+ break;
+ }
+ }
+ int delay = (iAmTheOnlyOne && !_in.isTruncated()) ? 0 : DNSConstants.RESPONSE_MIN_WAIT_INTERVAL + JmDNSImpl.getRandom().nextInt(DNSConstants.RESPONSE_MAX_WAIT_INTERVAL - DNSConstants.RESPONSE_MIN_WAIT_INTERVAL + 1) - _in.elapseSinceArrival();
+ if (delay < 0) {
+ delay = 0;
+ }
+ if (logger.isLoggable(Level.FINEST)) {
+ logger.finest(this.getName() + "start() Responder chosen delay=" + delay);
+ }
+ if (!this.getDns().isCanceling() && !this.getDns().isCanceled()) {
+ timer.schedule(this, delay);
+ }
+ }
+
+ @Override
+ public void run() {
+ this.getDns().respondToQuery(_in);
+
+ // We use these sets to prevent duplicate records
+ Set<DNSQuestion> questions = new HashSet<DNSQuestion>();
+ Set<DNSRecord> answers = new HashSet<DNSRecord>();
+
+ if (this.getDns().isAnnounced()) {
+ try {
+ // Answer questions
+ for (DNSQuestion question : _in.getQuestions()) {
+ if (logger.isLoggable(Level.FINER)) {
+ logger.finer(this.getName() + "run() JmDNS responding to: " + question);
+ }
+ // for unicast responses the question must be included
+ if (_unicast) {
+ // out.addQuestion(q);
+ questions.add(question);
+ }
+
+ question.addAnswers(this.getDns(), answers);
+ }
+
+ // remove known answers, if the ttl is at least half of the correct value. (See Draft Cheshire chapter 7.1.).
+ long now = System.currentTimeMillis();
+ for (DNSRecord knownAnswer : _in.getAnswers()) {
+ if (knownAnswer.isStale(now)) {
+ answers.remove(knownAnswer);
+ if (logger.isLoggable(Level.FINER)) {
+ logger.finer(this.getName() + "JmDNS Responder Known Answer Removed");
+ }
+ }
+ }
+
+ // respond if we have answers
+ if (!answers.isEmpty()) {
+ if (logger.isLoggable(Level.FINER)) {
+ logger.finer(this.getName() + "run() JmDNS responding");
+ }
+ DNSOutgoing out = new DNSOutgoing(DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA, !_unicast, _in.getSenderUDPPayload());
+ out.setId(_in.getId());
+ for (DNSQuestion question : questions) {
+ if (question != null) {
+ out = this.addQuestion(out, question);
+ }
+ }
+ for (DNSRecord answer : answers) {
+ if (answer != null) {
+ out = this.addAnswer(out, _in, answer);
+
+ }
+ }
+ if (!out.isEmpty()) this.getDns().send(out);
+ }
+ // this.cancel();
+ } catch (Throwable e) {
+ logger.log(Level.WARNING, this.getName() + "run() exception ", e);
+ this.getDns().close();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/javax/jmdns/impl/tasks/package-info.java b/src/javax/jmdns/impl/tasks/package-info.java
new file mode 100644
index 0000000..b8435ea
--- /dev/null
+++ b/src/javax/jmdns/impl/tasks/package-info.java
@@ -0,0 +1,2 @@
+package javax.jmdns.impl.tasks;
+
diff --git a/src/javax/jmdns/impl/tasks/resolver/DNSResolverTask.java b/src/javax/jmdns/impl/tasks/resolver/DNSResolverTask.java
new file mode 100644
index 0000000..7fa7abb
--- /dev/null
+++ b/src/javax/jmdns/impl/tasks/resolver/DNSResolverTask.java
@@ -0,0 +1,116 @@
+// Licensed under Apache License version 2.0
+package javax.jmdns.impl.tasks.resolver;
+
+import java.io.IOException;
+import java.util.Timer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.jmdns.impl.DNSOutgoing;
+import javax.jmdns.impl.JmDNSImpl;
+import javax.jmdns.impl.constants.DNSConstants;
+import javax.jmdns.impl.tasks.DNSTask;
+
+/**
+ * This is the root class for all resolver tasks.
+ *
+ * @author Pierre Frisch
+ */
+public abstract class DNSResolverTask extends DNSTask {
+ private static Logger logger = Logger.getLogger(DNSResolverTask.class.getName());
+
+ /**
+ * Counts the number of queries being sent.
+ */
+ protected int _count = 0;
+
+ /**
+ * @param jmDNSImpl
+ */
+ public DNSResolverTask(JmDNSImpl jmDNSImpl) {
+ super(jmDNSImpl);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return super.toString() + " count: " + _count;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.DNSTask#start(java.util.Timer)
+ */
+ @Override
+ public void start(Timer timer) {
+ if (!this.getDns().isCanceling() && !this.getDns().isCanceled()) {
+ timer.schedule(this, DNSConstants.QUERY_WAIT_INTERVAL, DNSConstants.QUERY_WAIT_INTERVAL);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.util.TimerTask#run()
+ */
+ @Override
+ public void run() {
+ try {
+ if (this.getDns().isCanceling() || this.getDns().isCanceled()) {
+ this.cancel();
+ } else {
+ if (_count++ < 3) {
+ if (logger.isLoggable(Level.FINER)) {
+ logger.finer(this.getName() + ".run() JmDNS " + this.description());
+ }
+ DNSOutgoing out = new DNSOutgoing(DNSConstants.FLAGS_QR_QUERY);
+ out = this.addQuestions(out);
+ if (this.getDns().isAnnounced()) {
+ out = this.addAnswers(out);
+ }
+ if (!out.isEmpty()) {
+ this.getDns().send(out);
+ }
+ } else {
+ // After three queries, we can quit.
+ this.cancel();
+ }
+ }
+ } catch (Throwable e) {
+ logger.log(Level.WARNING, this.getName() + ".run() exception ", e);
+ this.getDns().recover();
+ }
+ }
+
+ /**
+ * Overridden by subclasses to add questions to the message.<br/>
+ * <b>Note:</b> Because of message size limitation the returned message may be different than the message parameter.
+ *
+ * @param out
+ * outgoing message
+ * @return the outgoing message.
+ * @exception IOException
+ */
+ protected abstract DNSOutgoing addQuestions(DNSOutgoing out) throws IOException;
+
+ /**
+ * Overridden by subclasses to add questions to the message.<br/>
+ * <b>Note:</b> Because of message size limitation the returned message may be different than the message parameter.
+ *
+ * @param out
+ * outgoing message
+ * @return the outgoing message.
+ * @exception IOException
+ */
+ protected abstract DNSOutgoing addAnswers(DNSOutgoing out) throws IOException;
+
+ /**
+ * Returns a description of the resolver for debugging
+ *
+ * @return resolver description
+ */
+ protected abstract String description();
+
+}
diff --git a/src/javax/jmdns/impl/tasks/resolver/ServiceInfoResolver.java b/src/javax/jmdns/impl/tasks/resolver/ServiceInfoResolver.java
new file mode 100644
index 0000000..3147efd
--- /dev/null
+++ b/src/javax/jmdns/impl/tasks/resolver/ServiceInfoResolver.java
@@ -0,0 +1,102 @@
+// Copyright 2003-2005 Arthur van Hoff, Rick Blair
+// Licensed under Apache License version 2.0
+// Original license LGPL
+
+package javax.jmdns.impl.tasks.resolver;
+
+import java.io.IOException;
+
+import javax.jmdns.impl.DNSOutgoing;
+import javax.jmdns.impl.DNSQuestion;
+import javax.jmdns.impl.DNSRecord;
+import javax.jmdns.impl.JmDNSImpl;
+import javax.jmdns.impl.ServiceInfoImpl;
+import javax.jmdns.impl.constants.DNSRecordClass;
+import javax.jmdns.impl.constants.DNSRecordType;
+
+/**
+ * The ServiceInfoResolver queries up to three times consecutively for a service info, and then removes itself from the timer.
+ * <p/>
+ * The ServiceInfoResolver will run only if JmDNS is in state ANNOUNCED. REMIND: Prevent having multiple service resolvers for the same info in the timer queue.
+ */
+public class ServiceInfoResolver extends DNSResolverTask {
+
+ private final ServiceInfoImpl _info;
+
+ public ServiceInfoResolver(JmDNSImpl jmDNSImpl, ServiceInfoImpl info) {
+ super(jmDNSImpl);
+ this._info = info;
+ info.setDns(this.getDns());
+ this.getDns().addListener(info, DNSQuestion.newQuestion(info.getQualifiedName(), DNSRecordType.TYPE_ANY, DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.DNSTask#getName()
+ */
+ @Override
+ public String getName() {
+ return "ServiceInfoResolver(" + (this.getDns() != null ? this.getDns().getName() : "") + ")";
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.util.TimerTask#cancel()
+ */
+ @Override
+ public boolean cancel() {
+ // We should not forget to remove the listener
+ boolean result = super.cancel();
+ if (!_info.isPersistent()) {
+ this.getDns().removeListener(_info);
+ }
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.Resolver#addAnswers(javax.jmdns.impl.DNSOutgoing)
+ */
+ @Override
+ protected DNSOutgoing addAnswers(DNSOutgoing out) throws IOException {
+ DNSOutgoing newOut = out;
+ if (!_info.hasData()) {
+ long now = System.currentTimeMillis();
+ newOut = this.addAnswer(newOut, (DNSRecord) this.getDns().getCache().getDNSEntry(_info.getQualifiedName(), DNSRecordType.TYPE_SRV, DNSRecordClass.CLASS_IN), now);
+ newOut = this.addAnswer(newOut, (DNSRecord) this.getDns().getCache().getDNSEntry(_info.getQualifiedName(), DNSRecordType.TYPE_TXT, DNSRecordClass.CLASS_IN), now);
+ if (_info.getServer().length() > 0) {
+ newOut = this.addAnswer(newOut, (DNSRecord) this.getDns().getCache().getDNSEntry(_info.getServer(), DNSRecordType.TYPE_A, DNSRecordClass.CLASS_IN), now);
+ newOut = this.addAnswer(newOut, (DNSRecord) this.getDns().getCache().getDNSEntry(_info.getServer(), DNSRecordType.TYPE_AAAA, DNSRecordClass.CLASS_IN), now);
+ }
+ }
+ return newOut;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.Resolver#addQuestions(javax.jmdns.impl.DNSOutgoing)
+ */
+ @Override
+ protected DNSOutgoing addQuestions(DNSOutgoing out) throws IOException {
+ DNSOutgoing newOut = out;
+ if (!_info.hasData()) {
+ newOut = this.addQuestion(newOut, DNSQuestion.newQuestion(_info.getQualifiedName(), DNSRecordType.TYPE_SRV, DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE));
+ newOut = this.addQuestion(newOut, DNSQuestion.newQuestion(_info.getQualifiedName(), DNSRecordType.TYPE_TXT, DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE));
+ if (_info.getServer().length() > 0) {
+ newOut = this.addQuestion(newOut, DNSQuestion.newQuestion(_info.getServer(), DNSRecordType.TYPE_A, DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE));
+ newOut = this.addQuestion(newOut, DNSQuestion.newQuestion(_info.getServer(), DNSRecordType.TYPE_AAAA, DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE));
+ }
+ }
+ return newOut;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.Resolver#description()
+ */
+ @Override
+ protected String description() {
+ return "querying service info: " + (_info != null ? _info.getQualifiedName() : "null");
+ }
+
+} \ No newline at end of file
diff --git a/src/javax/jmdns/impl/tasks/resolver/ServiceResolver.java b/src/javax/jmdns/impl/tasks/resolver/ServiceResolver.java
new file mode 100644
index 0000000..ae7dee5
--- /dev/null
+++ b/src/javax/jmdns/impl/tasks/resolver/ServiceResolver.java
@@ -0,0 +1,77 @@
+// Copyright 2003-2005 Arthur van Hoff, Rick Blair
+// Licensed under Apache License version 2.0
+// Original license LGPL
+
+package javax.jmdns.impl.tasks.resolver;
+
+import java.io.IOException;
+
+import javax.jmdns.ServiceInfo;
+import javax.jmdns.impl.DNSOutgoing;
+import javax.jmdns.impl.DNSQuestion;
+import javax.jmdns.impl.DNSRecord;
+import javax.jmdns.impl.JmDNSImpl;
+import javax.jmdns.impl.constants.DNSConstants;
+import javax.jmdns.impl.constants.DNSRecordClass;
+import javax.jmdns.impl.constants.DNSRecordType;
+
+/**
+ * The ServiceResolver queries three times consecutively for services of a given type, and then removes itself from the timer.
+ * <p/>
+ * The ServiceResolver will run only if JmDNS is in state ANNOUNCED. REMIND: Prevent having multiple service resolvers for the same type in the timer queue.
+ */
+public class ServiceResolver extends DNSResolverTask {
+
+ private final String _type;
+
+ public ServiceResolver(JmDNSImpl jmDNSImpl, String type) {
+ super(jmDNSImpl);
+ this._type = type;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.DNSTask#getName()
+ */
+ @Override
+ public String getName() {
+ return "ServiceResolver(" + (this.getDns() != null ? this.getDns().getName() : "") + ")";
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.Resolver#addAnswers(javax.jmdns.impl.DNSOutgoing)
+ */
+ @Override
+ protected DNSOutgoing addAnswers(DNSOutgoing out) throws IOException {
+ DNSOutgoing newOut = out;
+ long now = System.currentTimeMillis();
+ for (ServiceInfo info : this.getDns().getServices().values()) {
+ newOut = this.addAnswer(newOut, new DNSRecord.Pointer(info.getType(), DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE, DNSConstants.DNS_TTL, info.getQualifiedName()), now);
+ // newOut = this.addAnswer(newOut, new DNSRecord.Service(info.getQualifiedName(), DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE, DNSConstants.DNS_TTL, info.getPriority(), info.getWeight(), info.getPort(),
+ // this.getDns().getLocalHost().getName()), now);
+ }
+ return newOut;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.Resolver#addQuestions(javax.jmdns.impl.DNSOutgoing)
+ */
+ @Override
+ protected DNSOutgoing addQuestions(DNSOutgoing out) throws IOException {
+ DNSOutgoing newOut = out;
+ newOut = this.addQuestion(newOut, DNSQuestion.newQuestion(_type, DNSRecordType.TYPE_PTR, DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE));
+ // newOut = this.addQuestion(newOut, DNSQuestion.newQuestion(_type, DNSRecordType.TYPE_SRV, DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE));
+ return newOut;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.Resolver#description()
+ */
+ @Override
+ protected String description() {
+ return "querying service";
+ }
+} \ No newline at end of file
diff --git a/src/javax/jmdns/impl/tasks/resolver/TypeResolver.java b/src/javax/jmdns/impl/tasks/resolver/TypeResolver.java
new file mode 100644
index 0000000..8382e54
--- /dev/null
+++ b/src/javax/jmdns/impl/tasks/resolver/TypeResolver.java
@@ -0,0 +1,75 @@
+// Copyright 2003-2005 Arthur van Hoff, Rick Blair
+// Licensed under Apache License version 2.0
+// Original license LGPL
+
+package javax.jmdns.impl.tasks.resolver;
+
+import java.io.IOException;
+
+import javax.jmdns.impl.DNSOutgoing;
+import javax.jmdns.impl.DNSQuestion;
+import javax.jmdns.impl.DNSRecord;
+import javax.jmdns.impl.JmDNSImpl;
+import javax.jmdns.impl.JmDNSImpl.ServiceTypeEntry;
+import javax.jmdns.impl.constants.DNSConstants;
+import javax.jmdns.impl.constants.DNSRecordClass;
+import javax.jmdns.impl.constants.DNSRecordType;
+
+/**
+ * Helper class to resolve service types.
+ * <p/>
+ * The TypeResolver queries three times consecutively for service types, and then removes itself from the timer.
+ * <p/>
+ * The TypeResolver will run only if JmDNS is in state ANNOUNCED.
+ */
+public class TypeResolver extends DNSResolverTask {
+
+ /**
+ * @param jmDNSImpl
+ */
+ public TypeResolver(JmDNSImpl jmDNSImpl) {
+ super(jmDNSImpl);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.DNSTask#getName()
+ */
+ @Override
+ public String getName() {
+ return "TypeResolver(" + (this.getDns() != null ? this.getDns().getName() : "") + ")";
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.Resolver#addAnswers(javax.jmdns.impl.DNSOutgoing)
+ */
+ @Override
+ protected DNSOutgoing addAnswers(DNSOutgoing out) throws IOException {
+ DNSOutgoing newOut = out;
+ long now = System.currentTimeMillis();
+ for (String type : this.getDns().getServiceTypes().keySet()) {
+ ServiceTypeEntry typeEntry = this.getDns().getServiceTypes().get(type);
+ newOut = this.addAnswer(newOut, new DNSRecord.Pointer("_services._dns-sd._udp.local.", DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE, DNSConstants.DNS_TTL, typeEntry.getType()), now);
+ }
+ return newOut;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.Resolver#addQuestions(javax.jmdns.impl.DNSOutgoing)
+ */
+ @Override
+ protected DNSOutgoing addQuestions(DNSOutgoing out) throws IOException {
+ return this.addQuestion(out, DNSQuestion.newQuestion("_services._dns-sd._udp.local.", DNSRecordType.TYPE_PTR, DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.Resolver#description()
+ */
+ @Override
+ protected String description() {
+ return "querying type";
+ }
+} \ No newline at end of file
diff --git a/src/javax/jmdns/impl/tasks/resolver/package-info.java b/src/javax/jmdns/impl/tasks/resolver/package-info.java
new file mode 100644
index 0000000..9fada6a
--- /dev/null
+++ b/src/javax/jmdns/impl/tasks/resolver/package-info.java
@@ -0,0 +1,2 @@
+package javax.jmdns.impl.tasks.resolver;
+
diff --git a/src/javax/jmdns/impl/tasks/state/Announcer.java b/src/javax/jmdns/impl/tasks/state/Announcer.java
new file mode 100644
index 0000000..aff60f7
--- /dev/null
+++ b/src/javax/jmdns/impl/tasks/state/Announcer.java
@@ -0,0 +1,146 @@
+// Copyright 2003-2005 Arthur van Hoff, Rick Blair
+// Licensed under Apache License version 2.0
+// Original license LGPL
+
+package javax.jmdns.impl.tasks.state;
+
+import java.io.IOException;
+import java.util.Timer;
+import java.util.logging.Logger;
+
+import javax.jmdns.impl.DNSOutgoing;
+import javax.jmdns.impl.DNSRecord;
+import javax.jmdns.impl.JmDNSImpl;
+import javax.jmdns.impl.ServiceInfoImpl;
+import javax.jmdns.impl.constants.DNSConstants;
+import javax.jmdns.impl.constants.DNSRecordClass;
+import javax.jmdns.impl.constants.DNSState;
+
+/**
+ * The Announcer sends an accumulated query of all announces, and advances the state of all serviceInfos, for which it has sent an announce. The Announcer also sends announcements and advances the state of JmDNS itself.
+ * <p/>
+ * When the announcer has run two times, it finishes.
+ */
+public class Announcer extends DNSStateTask {
+ static Logger logger = Logger.getLogger(Announcer.class.getName());
+
+ public Announcer(JmDNSImpl jmDNSImpl) {
+ super(jmDNSImpl, defaultTTL());
+
+ this.setTaskState(DNSState.ANNOUNCING_1);
+ this.associate(DNSState.ANNOUNCING_1);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.DNSTask#getName()
+ */
+ @Override
+ public String getName() {
+ return "Announcer(" + (this.getDns() != null ? this.getDns().getName() : "") + ")";
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return super.toString() + " state: " + this.getTaskState();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.DNSTask#start(java.util.Timer)
+ */
+ @Override
+ public void start(Timer timer) {
+ if (!this.getDns().isCanceling() && !this.getDns().isCanceled()) {
+ timer.schedule(this, DNSConstants.ANNOUNCE_WAIT_INTERVAL, DNSConstants.ANNOUNCE_WAIT_INTERVAL);
+ }
+ }
+
+ @Override
+ public boolean cancel() {
+ this.removeAssociation();
+
+ return super.cancel();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#getTaskDescription()
+ */
+ @Override
+ public String getTaskDescription() {
+ return "announcing";
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#checkRunCondition()
+ */
+ @Override
+ protected boolean checkRunCondition() {
+ return !this.getDns().isCanceling() && !this.getDns().isCanceled();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#createOugoing()
+ */
+ @Override
+ protected DNSOutgoing createOugoing() {
+ return new DNSOutgoing(DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#buildOutgoingForDNS(javax.jmdns.impl.DNSOutgoing)
+ */
+ @Override
+ protected DNSOutgoing buildOutgoingForDNS(DNSOutgoing out) throws IOException {
+ DNSOutgoing newOut = out;
+ for (DNSRecord answer : this.getDns().getLocalHost().answers(DNSRecordClass.UNIQUE, this.getTTL())) {
+ newOut = this.addAnswer(newOut, null, answer);
+ }
+ return newOut;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#buildOutgoingForInfo(javax.jmdns.impl.ServiceInfoImpl, javax.jmdns.impl.DNSOutgoing)
+ */
+ @Override
+ protected DNSOutgoing buildOutgoingForInfo(ServiceInfoImpl info, DNSOutgoing out) throws IOException {
+ DNSOutgoing newOut = out;
+ for (DNSRecord answer : info.answers(DNSRecordClass.UNIQUE, this.getTTL(), this.getDns().getLocalHost())) {
+ newOut = this.addAnswer(newOut, null, answer);
+ }
+ return newOut;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#recoverTask(java.lang.Throwable)
+ */
+ @Override
+ protected void recoverTask(Throwable e) {
+ this.getDns().recover();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#advanceTask()
+ */
+ @Override
+ protected void advanceTask() {
+ this.setTaskState(this.getTaskState().advance());
+ if (!this.getTaskState().isAnnouncing()) {
+ this.cancel();
+
+ this.getDns().startRenewer();
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/javax/jmdns/impl/tasks/state/Canceler.java b/src/javax/jmdns/impl/tasks/state/Canceler.java
new file mode 100644
index 0000000..50cf316
--- /dev/null
+++ b/src/javax/jmdns/impl/tasks/state/Canceler.java
@@ -0,0 +1,143 @@
+// Copyright 2003-2005 Arthur van Hoff, Rick Blair
+// Licensed under Apache License version 2.0
+// Original license LGPL
+
+package javax.jmdns.impl.tasks.state;
+
+import java.io.IOException;
+import java.util.Timer;
+import java.util.logging.Logger;
+
+import javax.jmdns.impl.DNSOutgoing;
+import javax.jmdns.impl.DNSRecord;
+import javax.jmdns.impl.JmDNSImpl;
+import javax.jmdns.impl.ServiceInfoImpl;
+import javax.jmdns.impl.constants.DNSConstants;
+import javax.jmdns.impl.constants.DNSRecordClass;
+import javax.jmdns.impl.constants.DNSState;
+
+/**
+ * The Canceler sends two announces with TTL=0 for the specified services.
+ */
+public class Canceler extends DNSStateTask {
+ static Logger logger = Logger.getLogger(Canceler.class.getName());
+
+ public Canceler(JmDNSImpl jmDNSImpl) {
+ super(jmDNSImpl, 0);
+
+ this.setTaskState(DNSState.CANCELING_1);
+ this.associate(DNSState.CANCELING_1);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.DNSTask#getName()
+ */
+ @Override
+ public String getName() {
+ return "Canceler(" + (this.getDns() != null ? this.getDns().getName() : "") + ")";
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return super.toString() + " state: " + this.getTaskState();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.DNSTask#start(java.util.Timer)
+ */
+ @Override
+ public void start(Timer timer) {
+ timer.schedule(this, 0, DNSConstants.ANNOUNCE_WAIT_INTERVAL);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.util.TimerTask#cancel()
+ */
+ @Override
+ public boolean cancel() {
+ this.removeAssociation();
+
+ return super.cancel();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#getTaskDescription()
+ */
+ @Override
+ public String getTaskDescription() {
+ return "canceling";
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#checkRunCondition()
+ */
+ @Override
+ protected boolean checkRunCondition() {
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#createOugoing()
+ */
+ @Override
+ protected DNSOutgoing createOugoing() {
+ return new DNSOutgoing(DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#buildOutgoingForDNS(javax.jmdns.impl.DNSOutgoing)
+ */
+ @Override
+ protected DNSOutgoing buildOutgoingForDNS(DNSOutgoing out) throws IOException {
+ DNSOutgoing newOut = out;
+ for (DNSRecord answer : this.getDns().getLocalHost().answers(DNSRecordClass.UNIQUE, this.getTTL())) {
+ newOut = this.addAnswer(newOut, null, answer);
+ }
+ return newOut;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#buildOutgoingForInfo(javax.jmdns.impl.ServiceInfoImpl, javax.jmdns.impl.DNSOutgoing)
+ */
+ @Override
+ protected DNSOutgoing buildOutgoingForInfo(ServiceInfoImpl info, DNSOutgoing out) throws IOException {
+ DNSOutgoing newOut = out;
+ for (DNSRecord answer : info.answers(DNSRecordClass.UNIQUE, this.getTTL(), this.getDns().getLocalHost())) {
+ newOut = this.addAnswer(newOut, null, answer);
+ }
+ return newOut;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#recoverTask(java.lang.Throwable)
+ */
+ @Override
+ protected void recoverTask(Throwable e) {
+ this.getDns().recover();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#advanceTask()
+ */
+ @Override
+ protected void advanceTask() {
+ this.setTaskState(this.getTaskState().advance());
+ if (!this.getTaskState().isCanceling()) {
+ cancel();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/javax/jmdns/impl/tasks/state/DNSStateTask.java b/src/javax/jmdns/impl/tasks/state/DNSStateTask.java
new file mode 100644
index 0000000..a0896d9
--- /dev/null
+++ b/src/javax/jmdns/impl/tasks/state/DNSStateTask.java
@@ -0,0 +1,188 @@
+// Licensed under Apache License version 2.0
+package javax.jmdns.impl.tasks.state;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.jmdns.ServiceInfo;
+import javax.jmdns.impl.DNSOutgoing;
+import javax.jmdns.impl.DNSStatefulObject;
+import javax.jmdns.impl.JmDNSImpl;
+import javax.jmdns.impl.ServiceInfoImpl;
+import javax.jmdns.impl.constants.DNSConstants;
+import javax.jmdns.impl.constants.DNSState;
+import javax.jmdns.impl.tasks.DNSTask;
+
+/**
+ * This is the root class for all state tasks. These tasks work with objects that implements the {@link javax.jmdns.impl.DNSStatefulObject} interface and therefore participate in the state machine.
+ *
+ * @author Pierre Frisch
+ */
+public abstract class DNSStateTask extends DNSTask {
+ static Logger logger1 = Logger.getLogger(DNSStateTask.class.getName());
+
+ /**
+ * By setting a 0 ttl we effectively expire the record.
+ */
+ private final int _ttl;
+
+ private static int _defaultTTL = DNSConstants.DNS_TTL;
+
+ /**
+ * The state of the task.
+ */
+ private DNSState _taskState = null;
+
+ public abstract String getTaskDescription();
+
+ public static int defaultTTL() {
+ return _defaultTTL;
+ }
+
+ /**
+ * For testing only do not use in production.
+ *
+ * @param value
+ */
+ public static void setDefaultTTL(int value) {
+ _defaultTTL = value;
+ }
+
+ /**
+ * @param jmDNSImpl
+ * @param ttl
+ */
+ public DNSStateTask(JmDNSImpl jmDNSImpl, int ttl) {
+ super(jmDNSImpl);
+ _ttl = ttl;
+ }
+
+ /**
+ * @return the ttl
+ */
+ public int getTTL() {
+ return _ttl;
+ }
+
+ /**
+ * Associate the DNS host and the service infos with this task if not already associated and in the same state.
+ *
+ * @param state
+ * target state
+ */
+ protected void associate(DNSState state) {
+ synchronized (this.getDns()) {
+ this.getDns().associateWithTask(this, state);
+ }
+ for (ServiceInfo serviceInfo : this.getDns().getServices().values()) {
+ ((ServiceInfoImpl) serviceInfo).associateWithTask(this, state);
+ }
+ }
+
+ /**
+ * Remove the DNS host and service info association with this task.
+ */
+ protected void removeAssociation() {
+ // Remove association from host to this
+ synchronized (this.getDns()) {
+ this.getDns().removeAssociationWithTask(this);
+ }
+
+ // Remove associations from services to this
+ for (ServiceInfo serviceInfo : this.getDns().getServices().values()) {
+ ((ServiceInfoImpl) serviceInfo).removeAssociationWithTask(this);
+ }
+ }
+
+ @Override
+ public void run() {
+ DNSOutgoing out = this.createOugoing();
+ try {
+ if (!this.checkRunCondition()) {
+ this.cancel();
+ return;
+ }
+ List<DNSStatefulObject> stateObjects = new ArrayList<DNSStatefulObject>();
+ // send probes for JmDNS itself
+ synchronized (this.getDns()) {
+ if (this.getDns().isAssociatedWithTask(this, this.getTaskState())) {
+ logger1.finer(this.getName() + ".run() JmDNS " + this.getTaskDescription() + " " + this.getDns().getName());
+ stateObjects.add(this.getDns());
+ out = this.buildOutgoingForDNS(out);
+ }
+ }
+ // send probes for services
+ for (ServiceInfo serviceInfo : this.getDns().getServices().values()) {
+ ServiceInfoImpl info = (ServiceInfoImpl) serviceInfo;
+
+ synchronized (info) {
+ if (info.isAssociatedWithTask(this, this.getTaskState())) {
+ logger1.fine(this.getName() + ".run() JmDNS " + this.getTaskDescription() + " " + info.getQualifiedName());
+ stateObjects.add(info);
+ out = this.buildOutgoingForInfo(info, out);
+ }
+ }
+ }
+ if (!out.isEmpty()) {
+ logger1.finer(this.getName() + ".run() JmDNS " + this.getTaskDescription() + " #" + this.getTaskState());
+ this.getDns().send(out);
+
+ // Advance the state of objects.
+ this.advanceObjectsState(stateObjects);
+ } else {
+ // Advance the state of objects.
+ this.advanceObjectsState(stateObjects);
+
+ // 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) {
+ logger1.log(Level.WARNING, this.getName() + ".run() exception ", e);
+ this.recoverTask(e);
+ }
+
+ this.advanceTask();
+ }
+
+ protected abstract boolean checkRunCondition();
+
+ protected abstract DNSOutgoing buildOutgoingForDNS(DNSOutgoing out) throws IOException;
+
+ protected abstract DNSOutgoing buildOutgoingForInfo(ServiceInfoImpl info, DNSOutgoing out) throws IOException;
+
+ protected abstract DNSOutgoing createOugoing();
+
+ protected void advanceObjectsState(List<DNSStatefulObject> list) {
+ if (list != null) {
+ for (DNSStatefulObject object : list) {
+ synchronized (object) {
+ object.advanceState(this);
+ }
+ }
+ }
+ }
+
+ protected abstract void recoverTask(Throwable e);
+
+ protected abstract void advanceTask();
+
+ /**
+ * @return the taskState
+ */
+ protected DNSState getTaskState() {
+ return this._taskState;
+ }
+
+ /**
+ * @param taskState
+ * the taskState to set
+ */
+ protected void setTaskState(DNSState taskState) {
+ this._taskState = taskState;
+ }
+
+}
diff --git a/src/javax/jmdns/impl/tasks/state/Prober.java b/src/javax/jmdns/impl/tasks/state/Prober.java
new file mode 100644
index 0000000..a793a27
--- /dev/null
+++ b/src/javax/jmdns/impl/tasks/state/Prober.java
@@ -0,0 +1,161 @@
+// Copyright 2003-2005 Arthur van Hoff, Rick Blair
+// Licensed under Apache License version 2.0
+// Original license LGPL
+
+package javax.jmdns.impl.tasks.state;
+
+import java.io.IOException;
+import java.util.Timer;
+import java.util.logging.Logger;
+
+import javax.jmdns.impl.DNSOutgoing;
+import javax.jmdns.impl.DNSQuestion;
+import javax.jmdns.impl.DNSRecord;
+import javax.jmdns.impl.JmDNSImpl;
+import javax.jmdns.impl.ServiceInfoImpl;
+import javax.jmdns.impl.constants.DNSConstants;
+import javax.jmdns.impl.constants.DNSRecordClass;
+import javax.jmdns.impl.constants.DNSRecordType;
+import javax.jmdns.impl.constants.DNSState;
+
+/**
+ * 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 to cancel itself.
+ */
+public class Prober extends DNSStateTask {
+ static Logger logger = Logger.getLogger(Prober.class.getName());
+
+ public Prober(JmDNSImpl jmDNSImpl) {
+ super(jmDNSImpl, defaultTTL());
+
+ this.setTaskState(DNSState.PROBING_1);
+ this.associate(DNSState.PROBING_1);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.DNSTask#getName()
+ */
+ @Override
+ public String getName() {
+ return "Prober(" + (this.getDns() != null ? this.getDns().getName() : "") + ")";
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return super.toString() + " state: " + this.getTaskState();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.DNSTask#start(java.util.Timer)
+ */
+ @Override
+ public void start(Timer timer) {
+ long now = System.currentTimeMillis();
+ if (now - this.getDns().getLastThrottleIncrement() < DNSConstants.PROBE_THROTTLE_COUNT_INTERVAL) {
+ this.getDns().setThrottle(this.getDns().getThrottle() + 1);
+ } else {
+ this.getDns().setThrottle(1);
+ }
+ this.getDns().setLastThrottleIncrement(now);
+
+ if (this.getDns().isAnnounced() && this.getDns().getThrottle() < DNSConstants.PROBE_THROTTLE_COUNT) {
+ timer.schedule(this, JmDNSImpl.getRandom().nextInt(1 + DNSConstants.PROBE_WAIT_INTERVAL), DNSConstants.PROBE_WAIT_INTERVAL);
+ } else if (!this.getDns().isCanceling() && !this.getDns().isCanceled()) {
+ timer.schedule(this, DNSConstants.PROBE_CONFLICT_INTERVAL, DNSConstants.PROBE_CONFLICT_INTERVAL);
+ }
+ }
+
+ @Override
+ public boolean cancel() {
+ this.removeAssociation();
+
+ return super.cancel();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#getTaskDescription()
+ */
+ @Override
+ public String getTaskDescription() {
+ return "probing";
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#checkRunCondition()
+ */
+ @Override
+ protected boolean checkRunCondition() {
+ return !this.getDns().isCanceling() && !this.getDns().isCanceled();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#createOugoing()
+ */
+ @Override
+ protected DNSOutgoing createOugoing() {
+ return new DNSOutgoing(DNSConstants.FLAGS_QR_QUERY);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#buildOutgoingForDNS(javax.jmdns.impl.DNSOutgoing)
+ */
+ @Override
+ protected DNSOutgoing buildOutgoingForDNS(DNSOutgoing out) throws IOException {
+ DNSOutgoing newOut = out;
+ newOut.addQuestion(DNSQuestion.newQuestion(this.getDns().getLocalHost().getName(), DNSRecordType.TYPE_ANY, DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE));
+ for (DNSRecord answer : this.getDns().getLocalHost().answers(DNSRecordClass.NOT_UNIQUE, this.getTTL())) {
+ newOut = this.addAuthoritativeAnswer(newOut, answer);
+ }
+ return newOut;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#buildOutgoingForInfo(javax.jmdns.impl.ServiceInfoImpl, javax.jmdns.impl.DNSOutgoing)
+ */
+ @Override
+ protected DNSOutgoing buildOutgoingForInfo(ServiceInfoImpl info, DNSOutgoing out) throws IOException {
+ DNSOutgoing newOut = out;
+ newOut = this.addQuestion(newOut, DNSQuestion.newQuestion(info.getQualifiedName(), DNSRecordType.TYPE_ANY, DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE));
+ // 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
+ newOut = this.addAuthoritativeAnswer(newOut, new DNSRecord.Service(info.getQualifiedName(), DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE, this.getTTL(), info.getPriority(), info.getWeight(), info.getPort(), this.getDns().getLocalHost()
+ .getName()));
+ return newOut;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#recoverTask(java.lang.Throwable)
+ */
+ @Override
+ protected void recoverTask(Throwable e) {
+ this.getDns().recover();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#advanceTask()
+ */
+ @Override
+ protected void advanceTask() {
+ this.setTaskState(this.getTaskState().advance());
+ if (!this.getTaskState().isProbing()) {
+ cancel();
+
+ this.getDns().startAnnouncer();
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/javax/jmdns/impl/tasks/state/Renewer.java b/src/javax/jmdns/impl/tasks/state/Renewer.java
new file mode 100644
index 0000000..f79ee12
--- /dev/null
+++ b/src/javax/jmdns/impl/tasks/state/Renewer.java
@@ -0,0 +1,144 @@
+// Copyright 2003-2005 Arthur van Hoff, Rick Blair
+// Licensed under Apache License version 2.0
+// Original license LGPL
+
+package javax.jmdns.impl.tasks.state;
+
+import java.io.IOException;
+import java.util.Timer;
+import java.util.logging.Logger;
+
+import javax.jmdns.impl.DNSOutgoing;
+import javax.jmdns.impl.DNSRecord;
+import javax.jmdns.impl.JmDNSImpl;
+import javax.jmdns.impl.ServiceInfoImpl;
+import javax.jmdns.impl.constants.DNSConstants;
+import javax.jmdns.impl.constants.DNSRecordClass;
+import javax.jmdns.impl.constants.DNSState;
+
+/**
+ * The Renewer is there to send renewal announcement when the record expire for ours infos.
+ */
+public class Renewer extends DNSStateTask {
+ static Logger logger = Logger.getLogger(Renewer.class.getName());
+
+ public Renewer(JmDNSImpl jmDNSImpl) {
+ super(jmDNSImpl, defaultTTL());
+
+ this.setTaskState(DNSState.ANNOUNCED);
+ this.associate(DNSState.ANNOUNCED);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.DNSTask#getName()
+ */
+ @Override
+ public String getName() {
+ return "Renewer(" + (this.getDns() != null ? this.getDns().getName() : "") + ")";
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return super.toString() + " state: " + this.getTaskState();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.DNSTask#start(java.util.Timer)
+ */
+ @Override
+ public void start(Timer timer) {
+ if (!this.getDns().isCanceling() && !this.getDns().isCanceled()) {
+ // BEGIN android-changed
+ // Schedule the renewer based on this task's TTL, not the default TTL
+ timer.schedule(this, getTTL() * 500, getTTL() * 500);
+ // END android-changed
+ }
+ }
+
+ @Override
+ public boolean cancel() {
+ this.removeAssociation();
+
+ return super.cancel();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#getTaskDescription()
+ */
+ @Override
+ public String getTaskDescription() {
+ return "renewing";
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#checkRunCondition()
+ */
+ @Override
+ protected boolean checkRunCondition() {
+ return !this.getDns().isCanceling() && !this.getDns().isCanceled();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#createOugoing()
+ */
+ @Override
+ protected DNSOutgoing createOugoing() {
+ return new DNSOutgoing(DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#buildOutgoingForDNS(javax.jmdns.impl.DNSOutgoing)
+ */
+ @Override
+ protected DNSOutgoing buildOutgoingForDNS(DNSOutgoing out) throws IOException {
+ DNSOutgoing newOut = out;
+ for (DNSRecord answer : this.getDns().getLocalHost().answers(DNSRecordClass.UNIQUE, this.getTTL())) {
+ newOut = this.addAnswer(newOut, null, answer);
+ }
+ return newOut;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#buildOutgoingForInfo(javax.jmdns.impl.ServiceInfoImpl, javax.jmdns.impl.DNSOutgoing)
+ */
+ @Override
+ protected DNSOutgoing buildOutgoingForInfo(ServiceInfoImpl info, DNSOutgoing out) throws IOException {
+ DNSOutgoing newOut = out;
+ for (DNSRecord answer : info.answers(DNSRecordClass.UNIQUE, this.getTTL(), this.getDns().getLocalHost())) {
+ newOut = this.addAnswer(newOut, null, answer);
+ }
+ return newOut;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#recoverTask(java.lang.Throwable)
+ */
+ @Override
+ protected void recoverTask(Throwable e) {
+ this.getDns().recover();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#advanceTask()
+ */
+ @Override
+ protected void advanceTask() {
+ this.setTaskState(this.getTaskState().advance());
+ if (!this.getTaskState().isAnnounced()) {
+ cancel();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/javax/jmdns/impl/tasks/state/package-info.java b/src/javax/jmdns/impl/tasks/state/package-info.java
new file mode 100644
index 0000000..d9b986c
--- /dev/null
+++ b/src/javax/jmdns/impl/tasks/state/package-info.java
@@ -0,0 +1,2 @@
+package javax.jmdns.impl.tasks.state;
+