org.saintandreas.serket.ssdp.Message.java Source code

Java tutorial

Introduction

Here is the source code for org.saintandreas.serket.ssdp.Message.java

Source

/*
 * Copyright (C) 2009 Bradley Austin Davis.
 * 
 * This file is part of serket.
 * 
 * serket is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * serket is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * serket. If not, see <http://www.gnu.org/licenses/>.
*/
package org.saintandreas.serket.ssdp;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.apache.http.Header;
import org.apache.http.HttpException;
import org.apache.http.impl.io.AbstractMessageParser;
import org.apache.http.impl.io.AbstractSessionInputBuffer;
import org.apache.http.io.SessionInputBuffer;
import org.apache.http.message.BasicLineParser;
import org.apache.http.message.LineParser;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;

import com.google.common.base.Charsets;

public class Message {

    public final static String NOTIFY_START = "NOTIFY * HTTP/1.1\r\n";
    public final static String SEARCH_START = "M-SEARCH * HTTP/1.1\r\n";
    public final static String HTTP_OK_START = "HTTP/1.1 200 OK\r\n";

    private final static String NOTIFY_TYPE_TEMPLATE = "NTS: %s\r\n";
    private final static String CACHE_CONTROL_TEMPLATE = "CACHE-CONTROL: max-age=%d\r\n";
    private final static String SEARCH_DISCOVER = "MAN: \"ssdp:discover\"\r\n";
    private final static String SYSTEM_IDENTIFIER = System.getProperty("os.name") + "/"
            + System.getProperty("os.version");
    private final static String NOTIFICATION_TYPE_TEMPLATE = "NT: %s\r\n";
    private final static String UNIQUE_SERVICE_NAME_TEMPLATE = "USN: %s\r\n";
    private final static String SERVER_TEMPLATE = "SERVER: " + SYSTEM_IDENTIFIER + " UPnP/1.1 %s\r\n";
    private final static String LOCATION_TEMPLATE = "LOCATION: %s\r\n";
    private final static String USER_AGENT_TEMPLATE = "USER-AGENT: " + SYSTEM_IDENTIFIER + " UPnP/1.1 %s\r\n";
    private final static String MULTICAST_HOST = "HOST: 239.255.255.250:1900\r\n";
    private final static String SEARCH_TARGET_TEMPLATE = "ST: %s\r\n";

    public static enum Type {
        NOTIFY_ALIVE, NOTIFY_BYEBYE, NOTIFY_UPDATE, SEARCH, RESPONSE
    }

    private final static String NOTIFY_TEMPLATE = NOTIFY_START + MULTICAST_HOST + NOTIFY_TYPE_TEMPLATE
            + NOTIFICATION_TYPE_TEMPLATE + UNIQUE_SERVICE_NAME_TEMPLATE;

    private final static String NOTIFY_ALIVE_TEMPLATE = LOCATION_TEMPLATE + CACHE_CONTROL_TEMPLATE + SERVER_TEMPLATE
            + "\r\n";

    //    private final static String NOTIFY_UPDATE_TEMPLATE = 
    //        NOTIFY_START + 
    //        MULTICAST_HOST + 
    //        NOTIFY_UPDATE +
    //        NOTIFICATION_TYPE_TEMPLATE +
    //        UNIQUE_SERVICE_NAME_TEMPLATE;
    //
    //    private final static String NOTIFY_BYEBYE_TEMPLATE = 
    //        NOTIFY_START + 
    //        MULTICAST_HOST + 
    //        NOTIFY_BYEBYE +
    //        NOTIFICATION_TYPE_TEMPLATE +
    //        UNIQUE_SERVICE_NAME_TEMPLATE;

    private final static String NOTIFY_ALIVE = "ssdp:alive";
    private final static String NOTIFY_UPDATE = "ssdp:update";
    private final static String NOTIFY_BYEBYE = "ssdp:byebyte";

    private static String buildNotifyMessage(String notificationMessageType, String notificationType,
            String uniqueServiceName) {
        return String.format(NOTIFY_TEMPLATE, notificationMessageType, notificationType, uniqueServiceName);
    }

    public static String buildNotifyAliveMessage(String notificationType, String uniqueServiceName, String location,
            long expireSeconds, String serverSuffix) {
        return buildNotifyMessage(NOTIFY_ALIVE, notificationType, uniqueServiceName)
                + String.format(NOTIFY_ALIVE_TEMPLATE, location, expireSeconds, serverSuffix) + "\r\n";
    }

    public static String buildNotifyUpdateMessage(String notificationType, String uniqueServiceName,
            String location, long expireSeconds, String serverSuffix) {
        return buildNotifyMessage(NOTIFY_UPDATE, notificationType, uniqueServiceName) + "\r\n";
    }

    public static String buildNotifyByeByeMessage(String notificationType, String uniqueServiceName,
            String location, long expireSeconds, String serverSuffix) {
        return buildNotifyMessage(NOTIFY_BYEBYE, notificationType, uniqueServiceName) + "\r\n";
    }

    private final static String SEARCH_TEAMPLTE = SEARCH_START + MULTICAST_HOST + SEARCH_DISCOVER
            + USER_AGENT_TEMPLATE + SEARCH_TARGET_TEMPLATE + "MX: %d\r\n" + "\r\n";

    public static String buildSearchMessage(String searchType, int seconds) {
        return String.format(SEARCH_TEAMPLTE, "serket/1.0", searchType, seconds);
    }

    private final static String SEARCH_RESPONSE_TEAMPLTE = HTTP_OK_START + UNIQUE_SERVICE_NAME_TEMPLATE
            + SEARCH_TARGET_TEMPLATE + LOCATION_TEMPLATE + CACHE_CONTROL_TEMPLATE + SERVER_TEMPLATE + "EXT:\r\n"
            + "\r\n";

    public static String buildSearchResponseMessage(String uniqueServiceName, String searchTarget, String location,
            long expireSeconds, String serverSuffix) {
        return String.format(SEARCH_RESPONSE_TEAMPLTE, uniqueServiceName, searchTarget, location, expireSeconds,
                serverSuffix);
    }

    public final Type type;
    public final Map<String, Header> headers;
    public final String usn;
    public final DatagramPacket original;
    public final String originalString;

    public Message(Type type, Map<String, Header> headers, String usn, DatagramPacket original,
            String originalString) {
        this.type = type;
        this.headers = Collections.unmodifiableMap(headers);
        this.usn = usn;
        this.original = original;
        this.originalString = originalString;
    }

    public static class ByteArrayInputBuffer extends AbstractSessionInputBuffer {
        private final ByteArrayInputStream is;

        public ByteArrayInputBuffer(byte[] buffer) {
            this(buffer, new BasicHttpParams());
        }

        public ByteArrayInputBuffer(byte[] buffer, HttpParams params) {
            this(buffer, 8192, params);
        }

        public ByteArrayInputBuffer(byte[] buffer, int bufferSize, HttpParams params) {
            is = new ByteArrayInputStream(buffer);
            this.init(is, bufferSize, params);
        }

        @Override
        public boolean isDataAvailable(int timeout) throws IOException {
            boolean result = hasBufferedData();
            if (!result) {
                fillBuffer();
                result = hasBufferedData();
            }
            return result;
        }

    }

    public static Message parseMessage(DatagramPacket packet) throws IOException, HttpException {
        Type type;
        Map<String, Header> headers = new HashMap<String, Header>();
        String usn = null;

        LineParser parser = new BasicLineParser();
        SessionInputBuffer inBuffer = new ByteArrayInputBuffer(packet.getData());
        String command = inBuffer.readLine();
        Header[] hs = AbstractMessageParser.parseHeaders(inBuffer, 64, 1024, parser);
        for (Header h : hs) {
            headers.put(h.getName(), h);
        }

        if (Message.NOTIFY_START.startsWith(command)) {
            Header h = headers.get("NTS");
            usn = headers.get("USN").getValue();
            if ("ssdp:alive".equals(h.getValue())) {
                // LogFactory.getLog(SSDPTests.class).debug("Notify message alive " + usn);
                type = Type.NOTIFY_ALIVE;
            } else if ("ssdp:update".equals(h.getValue())) {
                // LogFactory.getLog(SSDPTests.class).debug("Notify message update " + usn);
                type = Type.NOTIFY_UPDATE;
            } else if ("ssdp:byebye".equals(h.getValue())) {
                // LogFactory.getLog(SSDPTests.class).debug("Notify message byebye " + usn);
                type = Type.NOTIFY_BYEBYE;
            } else
                throw new RuntimeException("unknown type");
        } else if (Message.SEARCH_START.startsWith(command)) {
            usn = headers.get("ST").getValue();
            // LogFactory.getLog(SSDPTests.class).debug("Search message " + usn);
            type = Type.SEARCH;
        } else if (Message.HTTP_OK_START.startsWith(command)) {
            // LogFactory.getLog(SSDPTests.class).debug("Response message");
            type = Type.RESPONSE;
        } else
            throw new RuntimeException("unknown type");
        return new Message(type, headers, usn, packet,
                new String(packet.getData(), 0, packet.getLength(), Charsets.US_ASCII));
    }
}