Package javax.jmdns.impl

Source Code of javax.jmdns.impl.ServiceInfoImpl

//Copyright 2003-2005 Arthur van Hoff, Rick Blair
//Licensed under Apache License version 2.0
//Original license LGPL

package javax.jmdns.impl;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.TimerTask;
import java.util.Vector;
//import java.util.logging.Logger;

import javax.jmdns.ServiceInfo;
import javax.jmdns.impl.DNSRecord.Pointer;
import javax.jmdns.impl.DNSRecord.Service;
import javax.jmdns.impl.DNSRecord.Text;

/**
* JmDNS service information.
*
* @version %I%, %G%
* @author  Arthur van Hoff, Jeff Sonstein, Werner Randelshofer
*/
public class ServiceInfoImpl extends ServiceInfo implements DNSListener
{
//    private static Logger logger = Logger.getLogger(ServiceInfoImpl.class.getName());
    private JmDNSImpl dns;
   
    // State machine
    /**
     * The state of this service info.
     * This is used only for services announced by JmDNS.
     * <p/>
     * For proper handling of concurrency, this variable must be
     * changed only using methods advanceState(), revertState() and cancel().
     */
    private DNSState state = DNSState.PROBING_1;

    /**
     * Task associated to this service info.
     * Possible tasks are JmDNS.Prober, JmDNS.Announcer, JmDNS.Responder,
     * JmDNS.Canceler.
     */
    private TimerTask task;

    String type;
    private String name;
    String server;
    int port;
    int weight;
    int priority;
    private byte text[];
    Hashtable props;
    InetAddress addr;

    /**
     * @see javax.jmdns.ServiceInfo#create(String, String, int, String)
     */
    public ServiceInfoImpl(String type, String name, int port, String text)
    {
        this(type, name, port, 0, 0, text);
    }
   
    /**
     * @see javax.jmdns.ServiceInfo#create(String, String, int, int, int, String)
     */
    public ServiceInfoImpl(String type, String name, int port, int weight, int priority, String text)
    {
        this(type, name, port, weight, priority, (byte[]) null);
        try
        {
            ByteArrayOutputStream out = new ByteArrayOutputStream(text.length());
            writeUTF(out, text);
            byte [] data = out.toByteArray();
            this.setText(new byte[data.length + 1]);
            this.getText()[0] = (byte) data.length;
            System.arraycopy(data, 0, this.getText(), 1, data.length);
        }
        catch (IOException e)
        {
            throw new RuntimeException("unexpected exception: " + e);
        }
    }

    /**
     * @see javax.jmdns.ServiceInfo#create(String, String, int, int, int, Hashtable)
     */
    public ServiceInfoImpl(String type, String name, int port, int weight, int priority, Hashtable props)
    {
        this(type, name, port, weight, priority, new byte[0]);
        if (props != null)
        {
            try
            {
                ByteArrayOutputStream out = new ByteArrayOutputStream(256);
                for (Enumeration e = props.keys(); e.hasMoreElements();)
                {
                    String key = (String) e.nextElement();
                    Object val = props.get(key);
                    ByteArrayOutputStream out2 = new ByteArrayOutputStream(100);
                    writeUTF(out2, key);
                    if (val instanceof String)
                    {
                        out2.write('=');
                        writeUTF(out2, (String) val);
                    }
                    else
                    {
                        if (val instanceof byte[])
                        {
                            out2.write('=');
                            byte[] bval = (byte[]) val;
                            out2.write(bval, 0, bval.length);
                        }
                        else
                        {
                            if (val != NO_VALUE)
                            {
                                throw new IllegalArgumentException("invalid property value: " + val);
                            }
                        }
                    }
                    byte data[] = out2.toByteArray();
                    out.write(data.length);
                    out.write(data, 0, data.length);
                }
                this.setText(out.toByteArray());
            }
            catch (IOException e)
            {
                throw new RuntimeException("unexpected exception: " + e);
            }
        }
    }

    /**
     * @see javax.jmdns.ServiceInfo#create(String, String, int, int, int, byte[])
     */
    public ServiceInfoImpl(String type, String name, int port, int weight, int priority, byte text[])
    {
        this.type = type;
        this.name = name;
        this.port = port;
        this.weight = weight;
        this.priority = priority;
        this.setText(text);
    }

    /**
     * Construct a service record during service discovery.
     */
    ServiceInfoImpl(String type, String name)
    {
        if (!type.endsWith("."))
        {
            throw new IllegalArgumentException("type must be fully qualified DNS name ending in '.': " + type);
        }

        this.type = type;
        this.name = name;
    }

    /**
     * During recovery we need to duplicate service info to reregister them
     */
    ServiceInfoImpl(ServiceInfoImpl info)
    {
        if (info != null)
        {
            this.type = info.type;
            this.name = info.name;
            this.port = info.port;
            this.weight = info.weight;
            this.priority = info.priority;
            this.setText(info.getText());
        }
    }

    /**
     * @see javax.jmdns.ServiceInfo#getType()
     */
    public String getType()
    {
        return type;
    }

    /**
     * @see javax.jmdns.ServiceInfo#getName()
     */
    public String getName()
    {
        return name;
    }

    /**
     * Sets the service instance name.
     *
     * @param name unqualified service instance name, such as <code>foobar</code>
     */
    void setName(String name)
    {
        this.name = name;
    }

    /**
     * @see javax.jmdns.ServiceInfo#getQualifiedName()
     */
    public String getQualifiedName()
    {
        return name + "." + type;
    }

    /**
     * @see javax.jmdns.ServiceInfo#getServer()
     */
    public String getServer()
    {
        return server;
    }

    /**
     * @see javax.jmdns.ServiceInfo#getHostAddress()
     */
    public String getHostAddress()
    {
        return (addr != null ? addr.getHostAddress() : "");
    }

    public InetAddress getAddress()
    {
        return addr;
    }

    /**
     * @see javax.jmdns.ServiceInfo#getInetAddress()
     */
    public InetAddress getInetAddress()
    {
        return addr;
    }

    /**
     * @see javax.jmdns.ServiceInfo#getPort()
     */
    public int getPort()
    {
        return port;
    }

    /**
     * @see javax.jmdns.ServiceInfo#getPriority()
     */
    public int getPriority()
    {
        return priority;
    }

    /**
     * @see javax.jmdns.ServiceInfo#getWeight()
     */
    public int getWeight()
    {
        return weight;
    }

    /**
     * @see javax.jmdns.ServiceInfo#getTextBytes()
     */
    public byte[] getTextBytes()
    {
        return getText();
    }

    /**
     * @see javax.jmdns.ServiceInfo#getTextString()
     */
    public String getTextString()
    {
        if ((getText() == null) || (getText().length == 0) || ((getText().length == 1) && (getText()[0] == 0)))
        {
            return null;
        }
        return readUTF(getText(), 0, getText().length);
    }

    /**
     * @see javax.jmdns.ServiceInfo#getURL()
     */
    public String getURL()
    {
        return getURL("http");
    }

    /**
     * @see javax.jmdns.ServiceInfo#getURL(java.lang.String)
     */
    public String getURL(String protocol)
    {
        String url = protocol + "://" + getHostAddress() + ":" + getPort();
        String path = getPropertyString("path");
        if (path != null)
        {
            if (path.indexOf("://") >= 0)
            {
                url = path;
            }
            else
            {
                url += path.startsWith("/") ? path : "/" + path;
            }
        }
        return url;
    }

    /**
     * @see javax.jmdns.ServiceInfo#getPropertyBytes(java.lang.String)
     */
    public synchronized byte[] getPropertyBytes(String name)
    {
        return (byte[]) getProperties().get(name);
    }

    /**
     * @see javax.jmdns.ServiceInfo#getPropertyString(java.lang.String)
     */
    public synchronized String getPropertyString(String name)
    {
        byte data[] = (byte[]) getProperties().get(name);
        if (data == null)
        {
            return null;
        }
        if (data == NO_VALUE)
        {
            return "true";
        }
        return readUTF(data, 0, data.length);
    }

    /**
     * @see javax.jmdns.ServiceInfo#getPropertyNames()
     */
    public Enumeration getPropertyNames()
    {
        Hashtable props = getProperties();
        return (props != null) ? props.keys() : new Vector().elements();
    }

    /**
     * Write a UTF string with a length to a stream.
     */
    void writeUTF(OutputStream out, String str) throws IOException
    {
        for (int i = 0, len = str.length(); i < len; i++)
        {
            int c = str.charAt(i);
            if ((c >= 0x0001) && (c <= 0x007F))
            {
                out.write(c);
            }
            else
            {
                if (c > 0x07FF)
                {
                    out.write(0xE0 | ((c >> 12) & 0x0F));
                    out.write(0x80 | ((c >> 6) & 0x3F));
                    out.write(0x80 | ((c >> 0) & 0x3F));
                }
                else
                {
                    out.write(0xC0 | ((c >> 6) & 0x1F));
                    out.write(0x80 | ((c >> 0) & 0x3F));
                }
            }
        }
    }

    /**
     * Read data bytes as a UTF stream.
     */
    String readUTF(byte data[], int off, int len)
    {
        StringBuffer buf = new StringBuffer();
        for (int end = off + len; off < end;)
        {
            int ch = data[off++] & 0xFF;
            switch (ch >> 4)
            {
                case 0:
                case 1:
                case 2:
                case 3:
                case 4:
                case 5:
                case 6:
                case 7:
                    // 0xxxxxxx
                    break;
                case 12:
                case 13:
                    if (off >= len)
                    {
                        return null;
                    }
                    // 110x xxxx   10xx xxxx
                    ch = ((ch & 0x1F) << 6) | (data[off++] & 0x3F);
                    break;
                case 14:
                    if (off + 2 >= len)
                    {
                        return null;
                    }
                    // 1110 xxxx  10xx xxxx  10xx xxxx
                    ch = ((ch & 0x0f) << 12) | ((data[off++] & 0x3F) << 6) | (data[off++] & 0x3F);
                    break;
                default:
                    if (off + 1 >= len)
                    {
                        return null;
                    }
                    // 10xx xxxx,  1111 xxxx
                    ch = ((ch & 0x3F) << 4) | (data[off++] & 0x0f);
                    break;
            }
            buf.append((char) ch);
        }
        return buf.toString();
    }

    synchronized Hashtable getProperties()
    {
        if ((props == null) && (getText() != null))
        {
            Hashtable props = new Hashtable();
            int off = 0;
            while (off < getText().length)
            {
                // length of the next key value pair
                int len = getText()[off++] & 0xFF;
                if ((len == 0) || (off + len > getText().length))
                {
                    props.clear();
                    break;
                }
                // look for the '='
                int i = 0;
                for (; (i < len) && (getText()[off + i] != '='); i++)
                {
                    ;
                }

                // get the property name
                String name = readUTF(getText(), off, i);
                if (name == null)
                {
                    props.clear();
                    break;
                }
                if (i == len)
                {
                    props.put(name, NO_VALUE);
                }
                else
                {
                    byte value[] = new byte[len - ++i];
                    System.arraycopy(getText(), off + i, value, 0, len - i);
                    props.put(name, value);
                    off += len;
                }
            }
            this.props = props;
        }
        return props;
    }
   
    /**
     * JmDNS callback to update a DNS record.
     */
    public void updateRecord(JmDNSImpl jmdns, long now, DNSRecord rec)
    {
        if ((rec != null) && !rec.isExpired(now))
        {
            switch (rec.type)
            {
                case DNSConstants.TYPE_A:    // IPv4
                case DNSConstants.TYPE_AAAA:  // IPv6 FIXME [PJYF Oct 14 2004] This has not been tested
                    if (rec.name.equals(server))
                    {
                        addr = ((DNSRecord.Address) rec).getAddress();

                    }
                    break;
                case DNSConstants.TYPE_SRV:
                    if (rec.name.equals(getQualifiedName()))
                    {
                        DNSRecord.Service srv = (DNSRecord.Service) rec;
                        server = srv.server;
                        port = srv.port;
                        weight = srv.weight;
                        priority = srv.priority;
                        addr = null;
                        // changed to use getCache() instead - jeffs
                        // updateRecord(jmdns, now, (DNSRecord)jmdns.cache.get(server, TYPE_A, CLASS_IN));
                        updateRecord(jmdns, now, (DNSRecord) jmdns.getCache().get(server, DNSConstants.TYPE_A, DNSConstants.CLASS_IN));
                    }
                    break;
                case DNSConstants.TYPE_TXT:
                    if (rec.name.equals(getQualifiedName()))
                    {
                        DNSRecord.Text txt = (DNSRecord.Text) rec;
                        setText(txt.text);
                    }
                    break;
            }
            // Future Design Pattern
            // This is done, to notify the wait loop in method
            // JmDNS.getServiceInfo(type, name, timeout);
            if (hasData() && getDns() != null)
            {
                getDns().handleServiceResolved(this);
                setDns(null);
            }
            synchronized (this)
            {
                notifyAll();
            }
        }
    }

    /**
     * Returns true if the service info is filled with data.
     */
    public boolean hasData()
    {
        return server != null && addr != null && getText() != null;
    }
   
   
    // State machine
    /**
     * Sets the state and notifies all objects that wait on the ServiceInfo.
     */
    public synchronized void advanceState()
    {
        state = state.advance();
        notifyAll();
    }

    /**
     * Sets the state and notifies all objects that wait on the ServiceInfo.
     */
    synchronized void revertState()
    {
        state = state.revert();
        notifyAll();
    }

    /**
     * Sets the state and notifies all objects that wait on the ServiceInfo.
     */
    synchronized void cancel()
    {
        state = DNSState.CANCELED;
        notifyAll();
    }

    /**
     * Returns the current state of this info.
     */
    public DNSState getState()
    {
        return state;
    }


    public int hashCode()
    {
        return getQualifiedName().hashCode();
    }

    public boolean equals(Object obj)
    {
        return (obj instanceof ServiceInfoImpl) && getQualifiedName().equals(((ServiceInfoImpl) obj).getQualifiedName());
    }

    public String getNiceTextString()
    {
        StringBuffer buf = new StringBuffer();
        for (int i = 0, len = getText().length; i < len; i++)
        {
            if (i >= 20)
            {
                buf.append("...");
                break;
            }
            int ch = getText()[i] & 0xFF;
            if ((ch < ' ') || (ch > 127))
            {
                buf.append("\\0");
                buf.append(Integer.toString(ch, 8));
            }
            else
            {
                buf.append((char) ch);
            }
        }
        return buf.toString();
    }

    public String toString()
    {
        StringBuffer buf = new StringBuffer();
        buf.append("service[");
        buf.append(getQualifiedName());
        buf.append(',');
        buf.append(getAddress());
        buf.append(':');
        buf.append(port);
        buf.append(',');
        buf.append(getNiceTextString());
        buf.append(']');
        return buf.toString();
    }

  public void addAnswers(DNSOutgoing out, int ttl, HostInfo localHost) throws IOException
    {
        out.addAnswer(new Pointer(type, DNSConstants.TYPE_PTR, DNSConstants.CLASS_IN, ttl,
                getQualifiedName()), 0);
        out.addAnswer(new Service(getQualifiedName(), DNSConstants.TYPE_SRV, DNSConstants.CLASS_IN|DNSConstants.CLASS_UNIQUE,
                ttl, priority, weight, port, localHost.getName()), 0);
        out.addAnswer(new Text(getQualifiedName(), DNSConstants.TYPE_TXT, DNSConstants.CLASS_IN|DNSConstants.CLASS_UNIQUE,
                ttl, getText()), 0);
    }

    public void setTask(TimerTask task)
    {
        this.task = task;
    }

    public TimerTask getTask()
    {
        return task;
    }

    public void setText(byte [] text)
    {
        this.text = text;
    }

    public byte [] getText()
    {
        return text;
    }

    public void setDns(JmDNSImpl dns)
    {
        this.dns = dns;
    }

    public JmDNSImpl getDns()
    {
        return dns;
    }

  public String getDomain()
  {
    String protocol = getProtocol();
    int start = type.indexOf(protocol) + protocol.length() + 1;
    int end = type.length() - 1;
    return type.substring(start, end);
  }

  public String getProtocol()
  {
    int start = type.lastIndexOf("._") + 2;
    int end = type.indexOf('.', start);
    return type.substring(start, end);
  }
}
TOP

Related Classes of javax.jmdns.impl.ServiceInfoImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.