// 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());
private static int _defaultTTL = DNSConstants.DNS_TTL;
/**
* By setting a 0 ttl we effectively expire the record.
*/
private final int _ttl;
/**
* The state of the task.
*/
private DNSState _taskState = null;
/**
* @param jmDNSImpl
* @param ttl
*/
public DNSStateTask(JmDNSImpl jmDNSImpl, int ttl)
{
super(jmDNSImpl);
_ttl = ttl;
}
public static int defaultTTL()
{
return _defaultTTL;
}
/**
* For testing only do not use in production.
*
* @param value
*/
public static void setDefaultTTL(int value)
{
_defaultTTL = value;
}
public abstract String getTaskDescription();
/**
* @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;
}
}