13742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// Licensed under Apache License version 2.0
23742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanpackage javax.jmdns.impl.tasks.state;
33742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
43742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.io.IOException;
53742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.ArrayList;
63742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.List;
73742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.logging.Level;
83742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.logging.Logger;
93742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.ServiceInfo;
113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.DNSOutgoing;
123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.DNSStatefulObject;
133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.JmDNSImpl;
143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.ServiceInfoImpl;
153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.constants.DNSConstants;
163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.constants.DNSState;
173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.tasks.DNSTask;
183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman/**
203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman * 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.
213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman *
223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman * @author Pierre Frisch
233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman */
243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanpublic abstract class DNSStateTask extends DNSTask {
253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    static Logger      logger1     = Logger.getLogger(DNSStateTask.class.getName());
263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * By setting a 0 ttl we effectively expire the record.
293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private final int  _ttl;
313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private static int _defaultTTL = DNSConstants.DNS_TTL;
333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * The state of the task.
363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private DNSState   _taskState  = null;
383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public abstract String getTaskDescription();
403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public static int defaultTTL() {
423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return _defaultTTL;
433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * For testing only do not use in production.
473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param value
493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public static void setDefaultTTL(int value) {
513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        _defaultTTL = value;
523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param jmDNSImpl
563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param ttl
573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public DNSStateTask(JmDNSImpl jmDNSImpl, int ttl) {
593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        super(jmDNSImpl);
603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        _ttl = ttl;
613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @return the ttl
653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public int getTTL() {
673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return _ttl;
683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Associate the DNS host and the service infos with this task if not already associated and in the same state.
723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param state
743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *            target state
753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    protected void associate(DNSState state) {
773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        synchronized (this.getDns()) {
783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this.getDns().associateWithTask(this, state);
793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        for (ServiceInfo serviceInfo : this.getDns().getServices().values()) {
813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            ((ServiceInfoImpl) serviceInfo).associateWithTask(this, state);
823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Remove the DNS host and service info association with this task.
873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    protected void removeAssociation() {
893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // Remove association from host to this
903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        synchronized (this.getDns()) {
913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this.getDns().removeAssociationWithTask(this);
923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        // Remove associations from services to this
953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        for (ServiceInfo serviceInfo : this.getDns().getServices().values()) {
963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            ((ServiceInfoImpl) serviceInfo).removeAssociationWithTask(this);
973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
1013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void run() {
1023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        DNSOutgoing out = this.createOugoing();
1033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        try {
1043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (!this.checkRunCondition()) {
1053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                this.cancel();
1063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                return;
1073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
1083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            List<DNSStatefulObject> stateObjects = new ArrayList<DNSStatefulObject>();
1093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            // send probes for JmDNS itself
1103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            synchronized (this.getDns()) {
1113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (this.getDns().isAssociatedWithTask(this, this.getTaskState())) {
1123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    logger1.finer(this.getName() + ".run() JmDNS " + this.getTaskDescription() + " " + this.getDns().getName());
1133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    stateObjects.add(this.getDns());
1143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    out = this.buildOutgoingForDNS(out);
1153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
1163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
1173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            // send probes for services
1183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            for (ServiceInfo serviceInfo : this.getDns().getServices().values()) {
1193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                ServiceInfoImpl info = (ServiceInfoImpl) serviceInfo;
1203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                synchronized (info) {
1223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    if (info.isAssociatedWithTask(this, this.getTaskState())) {
1233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        logger1.fine(this.getName() + ".run() JmDNS " + this.getTaskDescription() + " " + info.getQualifiedName());
1243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        stateObjects.add(info);
1253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        out = this.buildOutgoingForInfo(info, out);
1263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    }
1273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
1283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
1293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (!out.isEmpty()) {
1303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                logger1.finer(this.getName() + ".run() JmDNS " + this.getTaskDescription() + " #" + this.getTaskState());
1313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                this.getDns().send(out);
1323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                // Advance the state of objects.
1343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                this.advanceObjectsState(stateObjects);
1353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            } else {
1363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                // Advance the state of objects.
1373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                this.advanceObjectsState(stateObjects);
1383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                // If we have nothing to send, another timer taskState ahead of us has done the job for us. We can cancel.
1403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                cancel();
1413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                return;
1423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
1433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        } catch (Throwable e) {
1443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            logger1.log(Level.WARNING, this.getName() + ".run() exception ", e);
1453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this.recoverTask(e);
1463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
1473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this.advanceTask();
1493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
1503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    protected abstract boolean checkRunCondition();
1523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    protected abstract DNSOutgoing buildOutgoingForDNS(DNSOutgoing out) throws IOException;
1543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    protected abstract DNSOutgoing buildOutgoingForInfo(ServiceInfoImpl info, DNSOutgoing out) throws IOException;
1563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    protected abstract DNSOutgoing createOugoing();
1583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    protected void advanceObjectsState(List<DNSStatefulObject> list) {
1603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (list != null) {
1613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            for (DNSStatefulObject object : list) {
1623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                synchronized (object) {
1633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    object.advanceState(this);
1643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
1653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
1663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
1673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
1683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    protected abstract void recoverTask(Throwable e);
1703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    protected abstract void advanceTask();
1723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
1743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @return the taskState
1753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
1763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    protected DNSState getTaskState() {
1773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return this._taskState;
1783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
1793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
1813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param taskState
1823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *            the taskState to set
1833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
1843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    protected void setTaskState(DNSState taskState) {
1853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this._taskState = taskState;
1863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
1873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman}
189