00001
00002
00003
00004
00005 package javax.jmdns.impl;
00006
00007 import java.io.ByteArrayOutputStream;
00008 import java.io.IOException;
00009 import java.io.OutputStream;
00010 import java.net.Inet4Address;
00011 import java.net.Inet6Address;
00012 import java.net.InetAddress;
00013 import java.util.ArrayList;
00014 import java.util.Collection;
00015 import java.util.Collections;
00016 import java.util.Enumeration;
00017 import java.util.HashMap;
00018 import java.util.Hashtable;
00019 import java.util.LinkedHashSet;
00020 import java.util.List;
00021 import java.util.Map;
00022 import java.util.Set;
00023 import java.util.Vector;
00024 import java.util.logging.Level;
00025 import java.util.logging.Logger;
00026
00027 import javax.jmdns.ServiceEvent;
00028 import javax.jmdns.ServiceInfo;
00029 import javax.jmdns.impl.DNSRecord.Pointer;
00030 import javax.jmdns.impl.DNSRecord.Service;
00031 import javax.jmdns.impl.DNSRecord.Text;
00032 import javax.jmdns.impl.constants.DNSRecordClass;
00033 import javax.jmdns.impl.constants.DNSRecordType;
00034 import javax.jmdns.impl.constants.DNSState;
00035 import javax.jmdns.impl.tasks.DNSTask;
00036
00042 public class ServiceInfoImpl extends ServiceInfo implements DNSListener, DNSStatefulObject {
00043 private static Logger logger = Logger.getLogger(ServiceInfoImpl.class.getName());
00044
00045 private String _domain;
00046 private String _protocol;
00047 private String _application;
00048 private String _name;
00049 private String _subtype;
00050 private String _server;
00051 private int _port;
00052 private int _weight;
00053 private int _priority;
00054 private byte _text[];
00055 private Map<String, byte[]> _props;
00056 private final Set<Inet4Address> _ipv4Addresses;
00057 private final Set<Inet6Address> _ipv6Addresses;
00058
00059 private transient String _key;
00060
00061 private boolean _persistent;
00062 private boolean _needTextAnnouncing;
00063
00064 private final ServiceInfoState _state;
00065
00066 private Delegate _delegate;
00067
00068 public static interface Delegate {
00069
00070 public void textValueUpdated(ServiceInfo target, byte[] value);
00071
00072 }
00073
00074 private final static class ServiceInfoState extends DNSStatefulObject.DefaultImplementation {
00075
00076 private static final long serialVersionUID = 1104131034952196820L;
00077
00078 private final ServiceInfoImpl _info;
00079
00083 public ServiceInfoState(ServiceInfoImpl info) {
00084 super();
00085 _info = info;
00086 }
00087
00088 @Override
00089 protected void setTask(DNSTask task) {
00090 super.setTask(task);
00091 if ((this._task == null) && _info.needTextAnnouncing()) {
00092 this.lock();
00093 try {
00094 if ((this._task == null) && _info.needTextAnnouncing()) {
00095 if (this._state.isAnnounced()) {
00096 this.setState(DNSState.ANNOUNCING_1);
00097 if (this.getDns() != null) {
00098 this.getDns().startAnnouncer();
00099 }
00100 }
00101 _info.setNeedTextAnnouncing(false);
00102 }
00103 } finally {
00104 this.unlock();
00105 }
00106 }
00107 }
00108
00109 @Override
00110 public void setDns(JmDNSImpl dns) {
00111 super.setDns(dns);
00112 }
00113
00114 }
00115
00127 public ServiceInfoImpl(String type, String name, String subtype, int port, int weight, int priority, boolean persistent, String text) {
00128 this(ServiceInfoImpl.decodeQualifiedNameMap(type, name, subtype), port, weight, priority, persistent, (byte[]) null);
00129 _server = text;
00130 try {
00131 ByteArrayOutputStream out = new ByteArrayOutputStream(text.length());
00132 writeUTF(out, text);
00133 this._text = out.toByteArray();
00134 } catch (IOException e) {
00135 throw new RuntimeException("unexpected exception: " + e);
00136 }
00137 }
00138
00150 public ServiceInfoImpl(String type, String name, String subtype, int port, int weight, int priority, boolean persistent, Map<String, ?> props) {
00151 this(ServiceInfoImpl.decodeQualifiedNameMap(type, name, subtype), port, weight, priority, persistent, textFromProperties(props));
00152 }
00153
00165 public ServiceInfoImpl(String type, String name, String subtype, int port, int weight, int priority, boolean persistent, byte text[]) {
00166 this(ServiceInfoImpl.decodeQualifiedNameMap(type, name, subtype), port, weight, priority, persistent, text);
00167 }
00168
00169 public ServiceInfoImpl(Map<Fields, String> qualifiedNameMap, int port, int weight, int priority, boolean persistent, Map<String, ?> props) {
00170 this(qualifiedNameMap, port, weight, priority, persistent, textFromProperties(props));
00171 }
00172
00173 ServiceInfoImpl(Map<Fields, String> qualifiedNameMap, int port, int weight, int priority, boolean persistent, String text) {
00174 this(qualifiedNameMap, port, weight, priority, persistent, (byte[]) null);
00175 _server = text;
00176 try {
00177 ByteArrayOutputStream out = new ByteArrayOutputStream(text.length());
00178 writeUTF(out, text);
00179 this._text = out.toByteArray();
00180 } catch (IOException e) {
00181 throw new RuntimeException("unexpected exception: " + e);
00182 }
00183 }
00184
00185 ServiceInfoImpl(Map<Fields, String> qualifiedNameMap, int port, int weight, int priority, boolean persistent, byte text[]) {
00186 Map<Fields, String> map = ServiceInfoImpl.checkQualifiedNameMap(qualifiedNameMap);
00187
00188 this._domain = map.get(Fields.Domain);
00189 this._protocol = map.get(Fields.Protocol);
00190 this._application = map.get(Fields.Application);
00191 this._name = map.get(Fields.Instance);
00192 this._subtype = map.get(Fields.Subtype);
00193
00194 this._port = port;
00195 this._weight = weight;
00196 this._priority = priority;
00197 this._text = text;
00198 this.setNeedTextAnnouncing(false);
00199 this._state = new ServiceInfoState(this);
00200 this._persistent = persistent;
00201 this._ipv4Addresses = Collections.synchronizedSet(new LinkedHashSet<Inet4Address>());
00202 this._ipv6Addresses = Collections.synchronizedSet(new LinkedHashSet<Inet6Address>());
00203 }
00204
00210 ServiceInfoImpl(ServiceInfo info) {
00211 this._ipv4Addresses = Collections.synchronizedSet(new LinkedHashSet<Inet4Address>());
00212 this._ipv6Addresses = Collections.synchronizedSet(new LinkedHashSet<Inet6Address>());
00213 if (info != null) {
00214 this._domain = info.getDomain();
00215 this._protocol = info.getProtocol();
00216 this._application = info.getApplication();
00217 this._name = info.getName();
00218 this._subtype = info.getSubtype();
00219 this._port = info.getPort();
00220 this._weight = info.getWeight();
00221 this._priority = info.getPriority();
00222 this._text = info.getTextBytes();
00223 this._persistent = info.isPersistent();
00224 Inet6Address[] ipv6Addresses = info.getInet6Addresses();
00225 for (Inet6Address address : ipv6Addresses) {
00226 this._ipv6Addresses.add(address);
00227 }
00228 Inet4Address[] ipv4Addresses = info.getInet4Addresses();
00229 for (Inet4Address address : ipv4Addresses) {
00230 this._ipv4Addresses.add(address);
00231 }
00232 }
00233 this._state = new ServiceInfoState(this);
00234 }
00235
00236 public static Map<Fields, String> decodeQualifiedNameMap(String type, String name, String subtype) {
00237 Map<Fields, String> qualifiedNameMap = decodeQualifiedNameMapForType(type);
00238
00239 qualifiedNameMap.put(Fields.Instance, name);
00240 qualifiedNameMap.put(Fields.Subtype, subtype);
00241
00242 return checkQualifiedNameMap(qualifiedNameMap);
00243 }
00244
00245 public static Map<Fields, String> decodeQualifiedNameMapForType(String type) {
00246 int index;
00247
00248 String casePreservedType = type;
00249
00250 String aType = type.toLowerCase();
00251 String application = aType;
00252 String protocol = "";
00253 String subtype = "";
00254 String name = "";
00255 String domain = "";
00256
00257 if (aType.contains("in-addr.arpa") || aType.contains("ip6.arpa")) {
00258 index = (aType.contains("in-addr.arpa") ? aType.indexOf("in-addr.arpa") : aType.indexOf("ip6.arpa"));
00259 name = removeSeparators(casePreservedType.substring(0, index));
00260 domain = casePreservedType.substring(index);
00261 application = "";
00262 } else if ((!aType.contains("_")) && aType.contains(".")) {
00263 index = aType.indexOf('.');
00264 name = removeSeparators(casePreservedType.substring(0, index));
00265 domain = removeSeparators(casePreservedType.substring(index));
00266 application = "";
00267 } else {
00268
00269 if (!aType.startsWith("_") || aType.startsWith("_services")) {
00270 index = aType.indexOf('.');
00271 if (index > 0) {
00272
00273 name = casePreservedType.substring(0, index);
00274 if (index + 1 < aType.length()) {
00275 aType = aType.substring(index + 1);
00276 casePreservedType = casePreservedType.substring(index + 1);
00277 }
00278 }
00279 }
00280
00281 index = aType.lastIndexOf("._");
00282 if (index > 0) {
00283 int start = index + 2;
00284 int end = aType.indexOf('.', start);
00285 protocol = casePreservedType.substring(start, end);
00286 }
00287 if (protocol.length() > 0) {
00288 index = aType.indexOf("_" + protocol.toLowerCase() + ".");
00289 int start = index + protocol.length() + 2;
00290 int end = aType.length() - (aType.endsWith(".") ? 1 : 0);
00291 if (end > start) {
00292 domain = casePreservedType.substring(start, end);
00293 }
00294 if (index > 0) {
00295 application = casePreservedType.substring(0, index - 1);
00296 } else {
00297 application = "";
00298 }
00299 }
00300 index = application.toLowerCase().indexOf("._sub");
00301 if (index > 0) {
00302 int start = index + 5;
00303 subtype = removeSeparators(application.substring(0, index));
00304 application = application.substring(start);
00305 }
00306 }
00307
00308 final Map<Fields, String> qualifiedNameMap = new HashMap<Fields, String>(5);
00309 qualifiedNameMap.put(Fields.Domain, removeSeparators(domain));
00310 qualifiedNameMap.put(Fields.Protocol, protocol);
00311 qualifiedNameMap.put(Fields.Application, removeSeparators(application));
00312 qualifiedNameMap.put(Fields.Instance, name);
00313 qualifiedNameMap.put(Fields.Subtype, subtype);
00314
00315 return qualifiedNameMap;
00316 }
00317
00318 protected static Map<Fields, String> checkQualifiedNameMap(Map<Fields, String> qualifiedNameMap) {
00319 Map<Fields, String> checkedQualifiedNameMap = new HashMap<Fields, String>(5);
00320
00321
00322 String domain = (qualifiedNameMap.containsKey(Fields.Domain) ? qualifiedNameMap.get(Fields.Domain) : "local");
00323 if ((domain == null) || (domain.length() == 0)) {
00324 domain = "local";
00325 }
00326 domain = removeSeparators(domain);
00327 checkedQualifiedNameMap.put(Fields.Domain, domain);
00328
00329 String protocol = (qualifiedNameMap.containsKey(Fields.Protocol) ? qualifiedNameMap.get(Fields.Protocol) : "tcp");
00330 if ((protocol == null) || (protocol.length() == 0)) {
00331 protocol = "tcp";
00332 }
00333 protocol = removeSeparators(protocol);
00334 checkedQualifiedNameMap.put(Fields.Protocol, protocol);
00335
00336 String application = (qualifiedNameMap.containsKey(Fields.Application) ? qualifiedNameMap.get(Fields.Application) : "");
00337 if ((application == null) || (application.length() == 0)) {
00338 application = "";
00339 }
00340 application = removeSeparators(application);
00341 checkedQualifiedNameMap.put(Fields.Application, application);
00342
00343 String instance = (qualifiedNameMap.containsKey(Fields.Instance) ? qualifiedNameMap.get(Fields.Instance) : "");
00344 if ((instance == null) || (instance.length() == 0)) {
00345 instance = "";
00346
00347 }
00348 instance = removeSeparators(instance);
00349 checkedQualifiedNameMap.put(Fields.Instance, instance);
00350
00351 String subtype = (qualifiedNameMap.containsKey(Fields.Subtype) ? qualifiedNameMap.get(Fields.Subtype) : "");
00352 if ((subtype == null) || (subtype.length() == 0)) {
00353 subtype = "";
00354 }
00355 subtype = removeSeparators(subtype);
00356 checkedQualifiedNameMap.put(Fields.Subtype, subtype);
00357
00358 return checkedQualifiedNameMap;
00359 }
00360
00361 private static String removeSeparators(String name) {
00362 if (name == null) {
00363 return "";
00364 }
00365 String newName = name.trim();
00366 if (newName.startsWith(".")) {
00367 newName = newName.substring(1);
00368 }
00369 if (newName.startsWith("_")) {
00370 newName = newName.substring(1);
00371 }
00372 if (newName.endsWith(".")) {
00373 newName = newName.substring(0, newName.length() - 1);
00374 }
00375 return newName;
00376 }
00377
00381 @Override
00382 public String getType() {
00383 String domain = this.getDomain();
00384 String protocol = this.getProtocol();
00385 String application = this.getApplication();
00386 return (application.length() > 0 ? "_" + application + "." : "") + (protocol.length() > 0 ? "_" + protocol + "." : "") + domain + ".";
00387 }
00388
00392 @Override
00393 public String getTypeWithSubtype() {
00394 String subtype = this.getSubtype();
00395 return (subtype.length() > 0 ? "_" + subtype.toLowerCase() + "._sub." : "") + this.getType();
00396 }
00397
00401 @Override
00402 public String getName() {
00403 return (_name != null ? _name : "");
00404 }
00405
00409 @Override
00410 public String getKey() {
00411 if (this._key == null) {
00412 this._key = this.getQualifiedName().toLowerCase();
00413 }
00414 return this._key;
00415 }
00416
00423 void setName(String name) {
00424 this._name = name;
00425 this._key = null;
00426 }
00427
00431 @Override
00432 public String getQualifiedName() {
00433 String domain = this.getDomain();
00434 String protocol = this.getProtocol();
00435 String application = this.getApplication();
00436 String instance = this.getName();
00437
00438
00439
00440 return (instance.length() > 0 ? instance + "." : "") + (application.length() > 0 ? "_" + application + "." : "") + (protocol.length() > 0 ? "_" + protocol + "." : "") + domain + ".";
00441 }
00442
00446 @Override
00447 public String getServer() {
00448 return (_server != null ? _server : "");
00449 }
00450
00455 void setServer(String server) {
00456 this._server = server;
00457 }
00458
00462 @Deprecated
00463 @Override
00464 public String getHostAddress() {
00465 String[] names = this.getHostAddresses();
00466 return (names.length > 0 ? names[0] : "");
00467 }
00468
00472 @Override
00473 public String[] getHostAddresses() {
00474 Inet4Address[] ip4Aaddresses = this.getInet4Addresses();
00475 Inet6Address[] ip6Aaddresses = this.getInet6Addresses();
00476 String[] names = new String[ip4Aaddresses.length + ip6Aaddresses.length];
00477 for (int i = 0; i < ip4Aaddresses.length; i++) {
00478 names[i] = ip4Aaddresses[i].getHostAddress();
00479 }
00480 for (int i = 0; i < ip6Aaddresses.length; i++) {
00481 names[i + ip4Aaddresses.length] = "[" + ip6Aaddresses[i].getHostAddress() + "]";
00482 }
00483 return names;
00484 }
00485
00490 void addAddress(Inet4Address addr) {
00491 _ipv4Addresses.add(addr);
00492 }
00493
00498 void addAddress(Inet6Address addr) {
00499 _ipv6Addresses.add(addr);
00500 }
00501
00505 @Deprecated
00506 @Override
00507 public InetAddress getAddress() {
00508 return this.getInetAddress();
00509 }
00510
00514 @Deprecated
00515 @Override
00516 public InetAddress getInetAddress() {
00517 InetAddress[] addresses = this.getInetAddresses();
00518 return (addresses.length > 0 ? addresses[0] : null);
00519 }
00520
00524 @Deprecated
00525 @Override
00526 public Inet4Address getInet4Address() {
00527 Inet4Address[] addresses = this.getInet4Addresses();
00528 return (addresses.length > 0 ? addresses[0] : null);
00529 }
00530
00534 @Deprecated
00535 @Override
00536 public Inet6Address getInet6Address() {
00537 Inet6Address[] addresses = this.getInet6Addresses();
00538 return (addresses.length > 0 ? addresses[0] : null);
00539 }
00540
00541
00542
00543
00544
00545 @Override
00546 public InetAddress[] getInetAddresses() {
00547 List<InetAddress> aList = new ArrayList<InetAddress>(_ipv4Addresses.size() + _ipv6Addresses.size());
00548 aList.addAll(_ipv4Addresses);
00549 aList.addAll(_ipv6Addresses);
00550 return aList.toArray(new InetAddress[aList.size()]);
00551 }
00552
00553
00554
00555
00556
00557 @Override
00558 public Inet4Address[] getInet4Addresses() {
00559 return _ipv4Addresses.toArray(new Inet4Address[_ipv4Addresses.size()]);
00560 }
00561
00562
00563
00564
00565
00566 @Override
00567 public Inet6Address[] getInet6Addresses() {
00568 return _ipv6Addresses.toArray(new Inet6Address[_ipv6Addresses.size()]);
00569 }
00570
00574 @Override
00575 public int getPort() {
00576 return _port;
00577 }
00578
00582 @Override
00583 public int getPriority() {
00584 return _priority;
00585 }
00586
00590 @Override
00591 public int getWeight() {
00592 return _weight;
00593 }
00594
00598 @Override
00599 public byte[] getTextBytes() {
00600 return (this._text != null && this._text.length > 0 ? this._text : DNSRecord.EMPTY_TXT);
00601 }
00602
00606 @Deprecated
00607 @Override
00608 public String getTextString() {
00609 Map<String, byte[]> properties = this.getProperties();
00610 for (String key : properties.keySet()) {
00611 byte[] value = properties.get(key);
00612 if ((value != null) && (value.length > 0)) {
00613 return key + "=" + new String(value);
00614 }
00615 return key;
00616 }
00617 return "";
00618 }
00619
00620
00621
00622
00623
00624 @Deprecated
00625 @Override
00626 public String getURL() {
00627 return this.getURL("http");
00628 }
00629
00630
00631
00632
00633
00634 @Override
00635 public String[] getURLs() {
00636 return this.getURLs("http");
00637 }
00638
00639
00640
00641
00642
00643 @Deprecated
00644 @Override
00645 public String getURL(String protocol) {
00646 String[] urls = this.getURLs(protocol);
00647 return (urls.length > 0 ? urls[0] : protocol + "://null:" + getPort());
00648 }
00649
00650
00651
00652
00653
00654 @Override
00655 public String[] getURLs(String protocol) {
00656 InetAddress[] addresses = this.getInetAddresses();
00657 String[] urls = new String[addresses.length];
00658 for (int i = 0; i < addresses.length; i++) {
00659 String url = protocol + "://" + addresses[i].getHostAddress() + ":" + getPort();
00660 String path = getPropertyString("path");
00661 if (path != null) {
00662 if (path.indexOf("://") >= 0) {
00663 url = path;
00664 } else {
00665 url += path.startsWith("/") ? path : "/" + path;
00666 }
00667 }
00668 urls[i] = url;
00669 }
00670 return urls;
00671 }
00672
00676 @Override
00677 public synchronized byte[] getPropertyBytes(String name) {
00678 return this.getProperties().get(name);
00679 }
00680
00684 @Override
00685 public synchronized String getPropertyString(String name) {
00686 byte data[] = this.getProperties().get(name);
00687 if (data == null) {
00688 return null;
00689 }
00690 if (data == NO_VALUE) {
00691 return "true";
00692 }
00693 return readUTF(data, 0, data.length);
00694 }
00695
00699 @Override
00700 public Enumeration<String> getPropertyNames() {
00701 Map<String, byte[]> properties = this.getProperties();
00702 Collection<String> names = (properties != null ? properties.keySet() : Collections.<String> emptySet());
00703 return new Vector<String>(names).elements();
00704 }
00705
00709 @Override
00710 public String getApplication() {
00711 return (_application != null ? _application : "");
00712 }
00713
00717 @Override
00718 public String getDomain() {
00719 return (_domain != null ? _domain : "local");
00720 }
00721
00725 @Override
00726 public String getProtocol() {
00727 return (_protocol != null ? _protocol : "tcp");
00728 }
00729
00733 @Override
00734 public String getSubtype() {
00735 return (_subtype != null ? _subtype : "");
00736 }
00737
00741 @Override
00742 public Map<Fields, String> getQualifiedNameMap() {
00743 Map<Fields, String> map = new HashMap<Fields, String>(5);
00744
00745 map.put(Fields.Domain, this.getDomain());
00746 map.put(Fields.Protocol, this.getProtocol());
00747 map.put(Fields.Application, this.getApplication());
00748 map.put(Fields.Instance, this.getName());
00749 map.put(Fields.Subtype, this.getSubtype());
00750 return map;
00751 }
00752
00756 static void writeUTF(OutputStream out, String str) throws IOException {
00757 for (int i = 0, len = str.length(); i < len; i++) {
00758 int c = str.charAt(i);
00759 if ((c >= 0x0001) && (c <= 0x007F)) {
00760 out.write(c);
00761 } else {
00762 if (c > 0x07FF) {
00763 out.write(0xE0 | ((c >> 12) & 0x0F));
00764 out.write(0x80 | ((c >> 6) & 0x3F));
00765 out.write(0x80 | ((c >> 0) & 0x3F));
00766 } else {
00767 out.write(0xC0 | ((c >> 6) & 0x1F));
00768 out.write(0x80 | ((c >> 0) & 0x3F));
00769 }
00770 }
00771 }
00772 }
00773
00777 String readUTF(byte data[], int off, int len) {
00778 int offset = off;
00779 StringBuffer buf = new StringBuffer();
00780 for (int end = offset + len; offset < end;) {
00781 int ch = data[offset++] & 0xFF;
00782 switch (ch >> 4) {
00783 case 0:
00784 case 1:
00785 case 2:
00786 case 3:
00787 case 4:
00788 case 5:
00789 case 6:
00790 case 7:
00791
00792 break;
00793 case 12:
00794 case 13:
00795 if (offset >= len) {
00796 return null;
00797 }
00798
00799 ch = ((ch & 0x1F) << 6) | (data[offset++] & 0x3F);
00800 break;
00801 case 14:
00802 if (offset + 2 >= len) {
00803 return null;
00804 }
00805
00806 ch = ((ch & 0x0f) << 12) | ((data[offset++] & 0x3F) << 6) | (data[offset++] & 0x3F);
00807 break;
00808 default:
00809 if (offset + 1 >= len) {
00810 return null;
00811 }
00812
00813 ch = ((ch & 0x3F) << 4) | (data[offset++] & 0x0f);
00814 break;
00815 }
00816 buf.append((char) ch);
00817 }
00818 return buf.toString();
00819 }
00820
00821 synchronized Map<String, byte[]> getProperties() {
00822 if ((_props == null) && (this.getTextBytes() != null)) {
00823 Hashtable<String, byte[]> properties = new Hashtable<String, byte[]>();
00824 try {
00825 int off = 0;
00826 while (off < getTextBytes().length) {
00827
00828 int len = getTextBytes()[off++] & 0xFF;
00829 if ((len == 0) || (off + len > getTextBytes().length)) {
00830 properties.clear();
00831 break;
00832 }
00833
00834 int i = 0;
00835 for (; (i < len) && (getTextBytes()[off + i] != '='); i++) {
00836
00837 }
00838
00839
00840 String name = readUTF(getTextBytes(), off, i);
00841 if (name == null) {
00842 properties.clear();
00843 break;
00844 }
00845 if (i == len) {
00846 properties.put(name, NO_VALUE);
00847 } else {
00848 byte value[] = new byte[len - ++i];
00849 System.arraycopy(getTextBytes(), off + i, value, 0, len - i);
00850 properties.put(name, value);
00851 off += len;
00852 }
00853 }
00854 } catch (Exception exception) {
00855
00856 logger.log(Level.WARNING, "Malformed TXT Field ", exception);
00857 }
00858 this._props = properties;
00859 }
00860 return (_props != null ? _props : Collections.<String, byte[]> emptyMap());
00861 }
00862
00870 @Override
00871 public void updateRecord(DNSCache dnsCache, long now, DNSEntry rec) {
00872 if ((rec instanceof DNSRecord) && !rec.isExpired(now)) {
00873 boolean serviceUpdated = false;
00874 String result;
00875 HostInfo host_info;
00876 switch (rec.getRecordType()) {
00877 case TYPE_A:
00878
00879
00880
00881
00882
00883
00884
00885
00886
00887
00888
00889
00890
00891
00892
00893
00894 if (rec.getName().equalsIgnoreCase(this.getServer())) {
00895 _ipv4Addresses.add((Inet4Address) ((DNSRecord.Address) rec).getAddress());
00896 serviceUpdated = true;
00897 }
00898 break;
00899 case TYPE_AAAA:
00900
00901
00902
00903
00904
00905
00906
00907
00908
00909
00910
00911
00912
00913
00914
00915
00916 if (rec.getName().equalsIgnoreCase(this.getServer())) {
00917 _ipv6Addresses.add((Inet6Address) ((DNSRecord.Address) rec).getAddress());
00918 serviceUpdated = true;
00919 }
00920 break;
00921 case TYPE_SRV:
00922
00923 if (rec.getName().equalsIgnoreCase(this.getQualifiedName())) {
00924 DNSRecord.Service srv = (DNSRecord.Service) rec;
00925
00926
00927
00928
00929
00930
00931
00932
00933
00934
00935
00936
00937
00938
00939
00940
00941
00942 boolean serverChanged = (_server == null) || !_server.equalsIgnoreCase(srv.getServer());
00943 _server = srv.getServer();
00944 _port = srv.getPort();
00945 _weight = srv.getWeight();
00946 _priority = srv.getPriority();
00947 if (serverChanged) {
00948 _ipv4Addresses.clear();
00949 _ipv6Addresses.clear();
00950 for (DNSEntry entry : dnsCache.getDNSEntryList(_server, DNSRecordType.TYPE_A, DNSRecordClass.CLASS_IN)) {
00951 this.updateRecord(dnsCache, now, entry);
00952 }
00953 for (DNSEntry entry : dnsCache.getDNSEntryList(_server, DNSRecordType.TYPE_AAAA, DNSRecordClass.CLASS_IN)) {
00954 this.updateRecord(dnsCache, now, entry);
00955 }
00956
00957 } else {
00958 serviceUpdated = true;
00959 }
00960 }
00961 break;
00962 case TYPE_TXT:
00963 if (rec.getName().equalsIgnoreCase(this.getQualifiedName())) {
00964 DNSRecord.Text txt = (DNSRecord.Text) rec;
00965 _text = txt.getText();
00966 _props = null;
00967 serviceUpdated = true;
00968 }
00969 break;
00970 case TYPE_PTR:
00971 if ((this.getSubtype().length() == 0) && (rec.getSubtype().length() != 0)) {
00972 _subtype = rec.getSubtype();
00973 serviceUpdated = true;
00974 }
00975 break;
00976 default:
00977 break;
00978 }
00979 if (serviceUpdated && this.hasData()) {
00980 JmDNSImpl dns = this.getDns();
00981 if (dns != null) {
00982 ServiceEvent event = ((DNSRecord) rec).getServiceEvent(dns);
00983 event = new ServiceEventImpl(dns, event.getType(), event.getName(), this);
00984 dns.handleServiceResolved(event);
00985 }
00986 }
00987
00988 synchronized (this) {
00989 this.notifyAll();
00990 }
00991 }
00992 }
00993
00999 @Override
01000 public synchronized boolean hasData() {
01001 return this.getServer() != null && this.hasInetAddress() && this.getTextBytes() != null && this.getTextBytes().length > 0;
01002
01003 }
01004
01005 private final boolean hasInetAddress() {
01006 return _ipv4Addresses.size() > 0 || _ipv6Addresses.size() > 0;
01007 }
01008
01009
01010
01014 @Override
01015 public boolean advanceState(DNSTask task) {
01016 return _state.advanceState(task);
01017 }
01018
01022 @Override
01023 public boolean revertState() {
01024 return _state.revertState();
01025 }
01026
01030 @Override
01031 public boolean cancelState() {
01032 return _state.cancelState();
01033 }
01034
01038 @Override
01039 public boolean closeState() {
01040 return this._state.closeState();
01041 }
01042
01046 @Override
01047 public boolean recoverState() {
01048 return this._state.recoverState();
01049 }
01050
01054 @Override
01055 public void removeAssociationWithTask(DNSTask task) {
01056 _state.removeAssociationWithTask(task);
01057 }
01058
01062 @Override
01063 public void associateWithTask(DNSTask task, DNSState state) {
01064 _state.associateWithTask(task, state);
01065 }
01066
01070 @Override
01071 public boolean isAssociatedWithTask(DNSTask task, DNSState state) {
01072 return _state.isAssociatedWithTask(task, state);
01073 }
01074
01078 @Override
01079 public boolean isProbing() {
01080 return _state.isProbing();
01081 }
01082
01086 @Override
01087 public boolean isAnnouncing() {
01088 return _state.isAnnouncing();
01089 }
01090
01094 @Override
01095 public boolean isAnnounced() {
01096 return _state.isAnnounced();
01097 }
01098
01102 @Override
01103 public boolean isCanceling() {
01104 return this._state.isCanceling();
01105 }
01106
01110 @Override
01111 public boolean isCanceled() {
01112 return _state.isCanceled();
01113 }
01114
01118 @Override
01119 public boolean isClosing() {
01120 return _state.isClosing();
01121 }
01122
01126 @Override
01127 public boolean isClosed() {
01128 return _state.isClosed();
01129 }
01130
01134 @Override
01135 public boolean waitForAnnounced(long timeout) {
01136 return _state.waitForAnnounced(timeout);
01137 }
01138
01142 @Override
01143 public boolean waitForCanceled(long timeout) {
01144 return _state.waitForCanceled(timeout);
01145 }
01146
01150 @Override
01151 public int hashCode() {
01152 return getQualifiedName().hashCode();
01153 }
01154
01158 @Override
01159 public boolean equals(Object obj) {
01160 return (obj instanceof ServiceInfoImpl) && getQualifiedName().equals(((ServiceInfoImpl) obj).getQualifiedName());
01161 }
01162
01166 @Override
01167 public String getNiceTextString() {
01168 StringBuffer buf = new StringBuffer();
01169 for (int i = 0, len = this.getTextBytes().length; i < len; i++) {
01170 if (i >= 200) {
01171 buf.append("...");
01172 break;
01173 }
01174 int ch = getTextBytes()[i] & 0xFF;
01175 if ((ch < ' ') || (ch > 127)) {
01176 buf.append("\\0");
01177 buf.append(Integer.toString(ch, 8));
01178 } else {
01179 buf.append((char) ch);
01180 }
01181 }
01182 return buf.toString();
01183 }
01184
01185
01186
01187
01188
01189 @Override
01190 public ServiceInfoImpl clone() {
01191 ServiceInfoImpl serviceInfo = new ServiceInfoImpl(this.getQualifiedNameMap(), _port, _weight, _priority, _persistent, _text);
01192 Inet6Address[] ipv6Addresses = this.getInet6Addresses();
01193 for (Inet6Address address : ipv6Addresses) {
01194 serviceInfo._ipv6Addresses.add(address);
01195 }
01196 Inet4Address[] ipv4Addresses = this.getInet4Addresses();
01197 for (Inet4Address address : ipv4Addresses) {
01198 serviceInfo._ipv4Addresses.add(address);
01199 }
01200 return serviceInfo;
01201 }
01202
01206 @Override
01207 public String toString() {
01208 StringBuilder buf = new StringBuilder();
01209 buf.append("[" + this.getClass().getSimpleName() + "@" + System.identityHashCode(this) + " ");
01210 buf.append("name: '");
01211 buf.append((this.getName().length() > 0 ? this.getName() + "." : "") + this.getTypeWithSubtype());
01212 buf.append("' address: '");
01213 InetAddress[] addresses = this.getInetAddresses();
01214 if (addresses.length > 0) {
01215 for (InetAddress address : addresses) {
01216 buf.append(address);
01217 buf.append(':');
01218 buf.append(this.getPort());
01219 buf.append(' ');
01220 }
01221 } else {
01222 buf.append("(null):");
01223 buf.append(this.getPort());
01224 }
01225 buf.append("' status: '");
01226 buf.append(_state.toString());
01227 buf.append(this.isPersistent() ? "' is persistent," : "',");
01228 buf.append(" has ");
01229 buf.append(this.hasData() ? "" : "NO ");
01230 buf.append("data");
01231 if (this.getTextBytes().length > 0) {
01232
01233
01234 Map<String, byte[]> properties = this.getProperties();
01235 if (!properties.isEmpty()) {
01236 buf.append("\n");
01237 for (String key : properties.keySet()) {
01238 buf.append("\t" + key + ": " + new String(properties.get(key)) + "\n");
01239 }
01240 } else {
01241 buf.append(" empty");
01242 }
01243 }
01244 buf.append(']');
01245 return buf.toString();
01246 }
01247
01248 public Collection<DNSRecord> answers(boolean unique, int ttl, HostInfo localHost) {
01249 List<DNSRecord> list = new ArrayList<DNSRecord>();
01250 if (this.getSubtype().length() > 0) {
01251 list.add(new Pointer(this.getTypeWithSubtype(), DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE, ttl, this.getQualifiedName()));
01252 }
01253 list.add(new Pointer(this.getType(), DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE, ttl, this.getQualifiedName()));
01254 list.add(new Service(this.getQualifiedName(), DNSRecordClass.CLASS_IN, unique, ttl, _priority, _weight, _port, localHost.getName()));
01255 list.add(new Text(this.getQualifiedName(), DNSRecordClass.CLASS_IN, unique, ttl, this.getTextBytes()));
01256 return list;
01257 }
01258
01262 @Override
01263 public void setText(byte[] text) throws IllegalStateException {
01264 synchronized (this) {
01265 this._text = text;
01266 this._props = null;
01267 this.setNeedTextAnnouncing(true);
01268 }
01269 }
01270
01274 @Override
01275 public void setText(Map<String, ?> props) throws IllegalStateException {
01276 this.setText(textFromProperties(props));
01277 }
01278
01284 void _setText(byte[] text) {
01285 this._text = text;
01286 this._props = null;
01287 }
01288
01289 private static byte[] textFromProperties(Map<String, ?> props) {
01290 byte[] text = null;
01291 if (props != null) {
01292 try {
01293 ByteArrayOutputStream out = new ByteArrayOutputStream(256);
01294 for (String key : props.keySet()) {
01295 Object val = props.get(key);
01296 ByteArrayOutputStream out2 = new ByteArrayOutputStream(100);
01297 writeUTF(out2, key);
01298 if (val == null) {
01299
01300 } else if (val instanceof String) {
01301 out2.write('=');
01302 writeUTF(out2, (String) val);
01303 } else if (val instanceof byte[]) {
01304 byte[] bval = (byte[]) val;
01305 if (bval.length > 0) {
01306 out2.write('=');
01307 out2.write(bval, 0, bval.length);
01308 } else {
01309 val = null;
01310 }
01311 } else {
01312 throw new IllegalArgumentException("invalid property value: " + val);
01313 }
01314 byte data[] = out2.toByteArray();
01315 if (data.length > 255) {
01316 throw new IOException("Cannot have individual values larger that 255 chars. Offending value: " + key + (val != null ? "" : "=" + val));
01317 }
01318 out.write((byte) data.length);
01319 out.write(data, 0, data.length);
01320 }
01321 text = out.toByteArray();
01322 } catch (IOException e) {
01323 throw new RuntimeException("unexpected exception: " + e);
01324 }
01325 }
01326 return (text != null && text.length > 0 ? text : DNSRecord.EMPTY_TXT);
01327 }
01328
01329 public void setDns(JmDNSImpl dns) {
01330 this._state.setDns(dns);
01331 }
01332
01336 @Override
01337 public JmDNSImpl getDns() {
01338 return this._state.getDns();
01339 }
01340
01344 @Override
01345 public boolean isPersistent() {
01346 return _persistent;
01347 }
01348
01353 public void setNeedTextAnnouncing(boolean needTextAnnouncing) {
01354 this._needTextAnnouncing = needTextAnnouncing;
01355 if (this._needTextAnnouncing) {
01356 _state.setTask(null);
01357 }
01358 }
01359
01363 public boolean needTextAnnouncing() {
01364 return _needTextAnnouncing;
01365 }
01366
01370 Delegate getDelegate() {
01371 return this._delegate;
01372 }
01373
01378 void setDelegate(Delegate delegate) {
01379 this._delegate = delegate;
01380 }
01381
01382 }