1 // Licensed under Apache License version 2.0 2 package javax.jmdns.impl.tasks.state; 3 4 import java.io.IOException; 5 import java.util.ArrayList; 6 import java.util.List; 7 import java.util.logging.Level; 8 import java.util.logging.Logger; 9 10 import javax.jmdns.ServiceInfo; 11 import javax.jmdns.impl.DNSOutgoing; 12 import javax.jmdns.impl.DNSStatefulObject; 13 import javax.jmdns.impl.JmDNSImpl; 14 import javax.jmdns.impl.ServiceInfoImpl; 15 import javax.jmdns.impl.constants.DNSConstants; 16 import javax.jmdns.impl.constants.DNSState; 17 import javax.jmdns.impl.tasks.DNSTask; 18 19 /** 20 * 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. 21 * 22 * @author Pierre Frisch 23 */ 24 public abstract class DNSStateTask extends DNSTask { 25 static Logger logger1 = Logger.getLogger(DNSStateTask.class.getName()); 26 27 /** 28 * By setting a 0 ttl we effectively expire the record. 29 */ 30 private final int _ttl; 31 32 private static int _defaultTTL = DNSConstants.DNS_TTL; 33 34 /** 35 * The state of the task. 36 */ 37 private DNSState _taskState = null; 38 39 public abstract String getTaskDescription(); 40 41 public static int defaultTTL() { 42 return _defaultTTL; 43 } 44 45 /** 46 * For testing only do not use in production. 47 * 48 * @param value 49 */ 50 public static void setDefaultTTL(int value) { 51 _defaultTTL = value; 52 } 53 54 /** 55 * @param jmDNSImpl 56 * @param ttl 57 */ 58 public DNSStateTask(JmDNSImpl jmDNSImpl, int ttl) { 59 super(jmDNSImpl); 60 _ttl = ttl; 61 } 62 63 /** 64 * @return the ttl 65 */ 66 public int getTTL() { 67 return _ttl; 68 } 69 70 /** 71 * Associate the DNS host and the service infos with this task if not already associated and in the same state. 72 * 73 * @param state 74 * target state 75 */ 76 protected void associate(DNSState state) { 77 synchronized (this.getDns()) { 78 this.getDns().associateWithTask(this, state); 79 } 80 for (ServiceInfo serviceInfo : this.getDns().getServices().values()) { 81 ((ServiceInfoImpl) serviceInfo).associateWithTask(this, state); 82 } 83 } 84 85 /** 86 * Remove the DNS host and service info association with this task. 87 */ 88 protected void removeAssociation() { 89 // Remove association from host to this 90 synchronized (this.getDns()) { 91 this.getDns().removeAssociationWithTask(this); 92 } 93 94 // Remove associations from services to this 95 for (ServiceInfo serviceInfo : this.getDns().getServices().values()) { 96 ((ServiceInfoImpl) serviceInfo).removeAssociationWithTask(this); 97 } 98 } 99 100 @Override 101 public void run() { 102 DNSOutgoing out = this.createOugoing(); 103 try { 104 if (!this.checkRunCondition()) { 105 this.cancel(); 106 return; 107 } 108 List<DNSStatefulObject> stateObjects = new ArrayList<DNSStatefulObject>(); 109 // send probes for JmDNS itself 110 synchronized (this.getDns()) { 111 if (this.getDns().isAssociatedWithTask(this, this.getTaskState())) { 112 logger1.finer(this.getName() + ".run() JmDNS " + this.getTaskDescription() + " " + this.getDns().getName()); 113 stateObjects.add(this.getDns()); 114 out = this.buildOutgoingForDNS(out); 115 } 116 } 117 // send probes for services 118 for (ServiceInfo serviceInfo : this.getDns().getServices().values()) { 119 ServiceInfoImpl info = (ServiceInfoImpl) serviceInfo; 120 121 synchronized (info) { 122 if (info.isAssociatedWithTask(this, this.getTaskState())) { 123 logger1.fine(this.getName() + ".run() JmDNS " + this.getTaskDescription() + " " + info.getQualifiedName()); 124 stateObjects.add(info); 125 out = this.buildOutgoingForInfo(info, out); 126 } 127 } 128 } 129 if (!out.isEmpty()) { 130 logger1.finer(this.getName() + ".run() JmDNS " + this.getTaskDescription() + " #" + this.getTaskState()); 131 this.getDns().send(out); 132 133 // Advance the state of objects. 134 this.advanceObjectsState(stateObjects); 135 } else { 136 // Advance the state of objects. 137 this.advanceObjectsState(stateObjects); 138 139 // If we have nothing to send, another timer taskState ahead of us has done the job for us. We can cancel. 140 cancel(); 141 return; 142 } 143 } catch (Throwable e) { 144 logger1.log(Level.WARNING, this.getName() + ".run() exception ", e); 145 this.recoverTask(e); 146 } 147 148 this.advanceTask(); 149 } 150 151 protected abstract boolean checkRunCondition(); 152 153 protected abstract DNSOutgoing buildOutgoingForDNS(DNSOutgoing out) throws IOException; 154 155 protected abstract DNSOutgoing buildOutgoingForInfo(ServiceInfoImpl info, DNSOutgoing out) throws IOException; 156 157 protected abstract DNSOutgoing createOugoing(); 158 159 protected void advanceObjectsState(List<DNSStatefulObject> list) { 160 if (list != null) { 161 for (DNSStatefulObject object : list) { 162 synchronized (object) { 163 object.advanceState(this); 164 } 165 } 166 } 167 } 168 169 protected abstract void recoverTask(Throwable e); 170 171 protected abstract void advanceTask(); 172 173 /** 174 * @return the taskState 175 */ 176 protected DNSState getTaskState() { 177 return this._taskState; 178 } 179 180 /** 181 * @param taskState 182 * the taskState to set 183 */ 184 protected void setTaskState(DNSState taskState) { 185 this._taskState = taskState; 186 } 187 188 } 189