ServiceInfoImpl.java
Go to the documentation of this file.
00001 // Copyright 2003-2005 Arthur van Hoff, Rick Blair
00002 // Licensed under Apache License version 2.0
00003 // Original license LGPL
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             // First remove the name if it there.
00269             if (!aType.startsWith("_") || aType.startsWith("_services")) {
00270                 index = aType.indexOf('.');
00271                 if (index > 0) {
00272                     // We need to preserve the case for the user readable name.
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         // Optional domain
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         // Optional protocol
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         // Application
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         // Instance
00343         String instance = (qualifiedNameMap.containsKey(Fields.Instance) ? qualifiedNameMap.get(Fields.Instance) : "");
00344         if ((instance == null) || (instance.length() == 0)) {
00345             instance = "";
00346             // throw new IllegalArgumentException("The instance name component of a fully qualified service cannot be empty.");
00347         }
00348         instance = removeSeparators(instance);
00349         checkedQualifiedNameMap.put(Fields.Instance, instance);
00350         // Optional Subtype
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         // String subtype = this.getSubtype();
00438         // return (instance.length() > 0 ? instance + "." : "") + (application.length() > 0 ? "_" + application + "." : "") + (protocol.length() > 0 ? "_" + protocol + (subtype.length() > 0 ? ",_" + subtype.toLowerCase() + "." : ".") : "") + domain
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      * (non-Javadoc)
00543      * @see javax.jmdns.ServiceInfo#getInetAddresses()
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      * (non-Javadoc)
00555      * @see javax.jmdns.ServiceInfo#getInet4Addresses()
00556      */
00557     @Override
00558     public Inet4Address[] getInet4Addresses() {
00559         return _ipv4Addresses.toArray(new Inet4Address[_ipv4Addresses.size()]);
00560     }
00561 
00562     /*
00563      * (non-Javadoc)
00564      * @see javax.jmdns.ServiceInfo#getInet6Addresses()
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      * (non-Javadoc)
00622      * @see javax.jmdns.ServiceInfo#getURL()
00623      */
00624     @Deprecated
00625     @Override
00626     public String getURL() {
00627         return this.getURL("http");
00628     }
00629 
00630     /*
00631      * (non-Javadoc)
00632      * @see javax.jmdns.ServiceInfo#getURLs()
00633      */
00634     @Override
00635     public String[] getURLs() {
00636         return this.getURLs("http");
00637     }
00638 
00639     /*
00640      * (non-Javadoc)
00641      * @see javax.jmdns.ServiceInfo#getURL(java.lang.String)
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      * (non-Javadoc)
00652      * @see javax.jmdns.ServiceInfo#getURLs(java.lang.String)
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                     // 0xxxxxxx
00792                     break;
00793                 case 12:
00794                 case 13:
00795                     if (offset >= len) {
00796                         return null;
00797                     }
00798                     // 110x xxxx 10xx xxxx
00799                     ch = ((ch & 0x1F) << 6) | (data[offset++] & 0x3F);
00800                     break;
00801                 case 14:
00802                     if (offset + 2 >= len) {
00803                         return null;
00804                     }
00805                     // 1110 xxxx 10xx xxxx 10xx xxxx
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                     // 10xx xxxx, 1111 xxxx
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                     // length of the next key value pair
00828                     int len = getTextBytes()[off++] & 0xFF;
00829                     if ((len == 0) || (off + len > getTextBytes().length)) {
00830                         properties.clear();
00831                         break;
00832                     }
00833                     // look for the '='
00834                     int i = 0;
00835                     for (; (i < len) && (getTextBytes()[off + i] != '='); i++) {
00836                         /* Stub */
00837                     }
00838 
00839                     // get the property name
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                 // We should get better logging.
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: // IPv4
00878 //                      {
00879 //                              result = "UpdateRecord (TYPE_IPv4):\n";
00880 //                              host_info = this.getDns().getLocalHost();
00881 //                              result += "    Record Jmdns : " + host_info.getInetAddress().getHostAddress();
00882 //                              result += " [" + host_info.getInterface().getDisplayName() + "]\n";
00883 //                              result += "    Record Name  : " + rec.getName() + "\n";
00884 //                              result += "    Info Server  : " + this.getServer() + "\n";
00885 //                              for ( Inet4Address address : _ipv4Addresses ) {
00886 //                                      result += "    Address     : " + address.getHostAddress() + "\n";
00887 //                              }
00888 //                              for ( Inet6Address address : _ipv6Addresses ) {
00889 //                                      result += "    Address     : " + address.getHostAddress() + "\n";;
00890 //                              }
00891 //                                      result += "    [+] Address  : " + ((Inet4Address) ((DNSRecord.Address) rec).getAddress()).getHostAddress() + "\n";
00892 //                                      System.out.printf("%s", result);
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: // IPv6
00900 //                      {
00901 //                              result = "UpdateRecord (TYPE_IPv6):\n";
00902 //                              host_info = this.getDns().getLocalHost();
00903 //                              result += "    Record Jmdns : " + host_info.getInetAddress().getHostAddress();
00904 //                              result += " [" + host_info.getInterface().getDisplayName() + "]\n";
00905 //                              result += "    Record Name  : " + rec.getName() + "\n";
00906 //                              result += "    Info Server  : " + this.getServer() + "\n";
00907 //                              for ( Inet4Address address : _ipv4Addresses ) {
00908 //                                      result += "    Address     : " + address.getHostAddress() + "\n";
00909 //                              }
00910 //                              for ( Inet6Address address : _ipv6Addresses ) {
00911 //                                      result += "    Address     : " + address.getHostAddress() + "\n";;
00912 //                              }
00913 //                                      result += "    [+] Address  : " + ((DNSRecord.Address) rec).getAddress().getHostAddress() + "\n";
00914 //                                      System.out.printf("%s", result);
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                     // do a git checkout 6e46cc136a95849d2799a857da1ac851046aadb9 to see my first experiment here to solve multi-interface issues
00923                     if (rec.getName().equalsIgnoreCase(this.getQualifiedName())) {
00924                                 DNSRecord.Service srv = (DNSRecord.Service) rec;
00925 
00926 //                              { // debugging
00927 //                              result = "UpdateRecord (TYPE_SRV):\n";
00928 //                                      host_info = this.getDns().getLocalHost();
00929 //                                      result += "    Record Jmdns: " + host_info.getInetAddress().getHostAddress();
00930 //                                      result += " [" + host_info.getInterface().getDisplayName() + "]\n";
00931 //                                      result += "    DNS Server  : " + srv.getServer() + "\n";
00932 //                                      if ( _server == null ) {
00933 //                                              result += "    Info Server : -\n";
00934 //                                      } else {
00935 //                                              result += "    Info Server : " + _server + "\n";
00936 //                                      }
00937 //                                      System.out.printf("%s", result);
00938 //                      }
00939                                 
00940                         // DJS: This is the original jmdns, but it fails on multiple interface pc's.
00941                         // Issue: http://sourceforge.net/tracker/?func=detail&aid=3437568&group_id=93852&atid=605791
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                             // We do not want to trigger the listener in this case as it will be triggered if the address resolves.
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; // set it null for apply update text data
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             // This is done, to notify the wait loop in method JmDNS.waitForInfoData(ServiceInfo info, int timeout);
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         // return this.getServer() != null && (this.getAddress() != null || (this.getTextBytes() != null && this.getTextBytes().length > 0));
01003     }
01004 
01005     private final boolean hasInetAddress() {
01006         return _ipv4Addresses.size() > 0 || _ipv6Addresses.size() > 0;
01007     }
01008 
01009     // State machine
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      * (non-Javadoc)
01187      * @see javax.jmdns.ServiceInfo#clone()
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             // buf.append("\n");
01233             // buf.append(this.getNiceTextString());
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                         // Skip
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 }


zeroconf_jmdns_suite
Author(s): Daniel Stonier
autogenerated on Thu Aug 27 2015 15:50:27