Package ch.ethz.iks.slp.impl

Source Code of ch.ethz.iks.slp.impl.SLPMessage

/****************************************************************************
* Copyright (c) 2005, 2010 Jan S. Rellermeyer, Systems Group,
* Department of Computer Science, ETH Zurich and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*    Jan S. Rellermeyer - initial API and implementation
*    Markus Alexander Kuppe - enhancements and bug fixes
*
*****************************************************************************/
package ch.ethz.iks.slp.impl;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ProtocolException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;

import ch.ethz.iks.slp.ServiceLocationException;
import ch.ethz.iks.slp.impl.attr.AttributeListVisitor;
import ch.ethz.iks.slp.impl.attr.gen.Parser;
import ch.ethz.iks.slp.impl.attr.gen.ParserException;
import ch.ethz.iks.slp.impl.attr.gen.Rule;

/**
* base class for all messages that the SLP framework uses.
*
* @author Jan S. Rellermeyer, ETH Zurich
* @since 0.1
*/
public abstract class SLPMessage {

  /**
   * the <code>Locale</code> of the message.
   */
  Locale locale;

  /**
   * the funcID encodes the message type.
   */
  byte funcID;

  /**
   * the transaction ID.
   */
  short xid;

  /**
   * the sender or receiver address.
   */
  InetAddress address;

  /**
   * the sender or receiver port.
   */
  int port;

  /**
   * true if the message was processed or will be sent via TCP
   */
  boolean tcp;

  /**
   * true if the message came in or will go out by multicast.
   */
  boolean multicast;


  /**
   * the message version according to RFC 2608, Version = 2.
   */
  public static final byte VERSION = 2;
 
  /**
   * the message funcID values according to RFC 2608, Service Request = 1.
   */
  public static final byte SRVRQST = 1;

  /**
   * the message funcID values according to RFC 2608, Service Reply = 2.
   */
  public static final byte SRVRPLY = 2;

  /**
   * the message funcID values according to RFC 2608, Service Registration =
   * 3.
   */
  public static final byte SRVREG = 3;

  /**
   * the message funcID values according to RFC 2608, Service Deregistration =
   * 4.
   */
  public static final byte SRVDEREG = 4;

  /**
   * the message funcID values according to RFC 2608, Service Acknowledgement =
   * 5.
   */
  public static final byte SRVACK = 5;

  /**
   * the message funcID values according to RFC 2608, Attribute Request = 6.
   */
  public static final byte ATTRRQST = 6;

  /**
   * the message funcID values according to RFC 2608, Attribute Reply = 7.
   */
  public static final byte ATTRRPLY = 7;

  /**
   * the message funcID values according to RFC 2608, DA Advertisement = 8.
   */
  public static final byte DAADVERT = 8;

  /**
   * the message funcID values according to RFC 2608, Service Type Request =
   * 9.
   */
  public static final byte SRVTYPERQST = 9;

  /**
   * the message funcID values according to RFC 2608, Service Type Reply = 10.
   */
  public static final byte SRVTYPERPLY = 10;

  /**
   * the message funcID values according to RFC 2608, SA Advertisement = 11.
   */
  public static final byte SAADVERT = 11;

  /**
   * used for reverse lookup of funcID values to have nicer debug messages.
   */
  private static final String[] TYPES = { "NULL", "SRVRQST", "SRVPLY",
      "SRVREG", "SRVDEREG", "SRVACK", "ATTRRQST", "ATTRRPLY", "DAADVERT",
      "SRVTYPERQST", "SRVTYPERPLY", "SAADVERT" };

  /**
   * get the bytes from a SLPMessage. Processes the header and then calls the
   * getBody() method of the implementing subclass.
   *
   * @return an array of bytes encoding the SLPMessage.
   * @throws IOException
   * @throws ServiceLocationException
   *             in case of IOExceptions.
   */
  private void writeHeader(final DataOutputStream out)
      throws IOException {
    byte flags = 0;
    if (funcID == SRVREG) {
      flags |= 0x40;
    }
    if (multicast) {
      flags |= 0x20;
    }
    int msgSize = getSize();
    if (!tcp && msgSize > SLPCore.CONFIG.getMTU()) {
      flags |= 0x80;
    }
    out.write(VERSION);
    out.write(funcID);
    out.write((byte) ((msgSize) >> 16));
    out.write((byte) (((msgSize) >> 8) & 0xFF));
    out.write((byte) ((msgSize) & 0xFF));
    out.write(flags);
    out.write(0);
    out.write(0);
    out.write(0);
    out.write(0);
    out.writeShort(xid);
    out.writeUTF(locale.getLanguage());
  }

  /**
   *
   */
  protected abstract void writeTo(final DataOutputStream out) throws IOException;

  /**
   *
   */
  byte[] getBytes() throws IOException {
    final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    final DataOutputStream out = new DataOutputStream(bytes);
    writeHeader(out);
    writeTo(out);
    return bytes.toByteArray();
  }

  /**
   * The RFC 2608 SLP message header:
   *
   * <pre>
   *                   0                   1                   2                   3
   *                   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   *                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   *                  |    Version    |  Function-ID  |            Length             |
   *                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   *                  | Length, contd.|O|F|R|       reserved          |Next Ext Offset|
   *                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   *                  |  Next Extension Offset, contd.|              XID              |
   *                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   *                  |      Language Tag Length      |         Language Tag          \
   *                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   * </pre>
   *
   * This method parses the header and then delegates the creation of the
   * corresponding SLPMessage to the subclass that matches the funcID.
   *
   * @param senderAddr
   *            the address of the message sender.
   * @param senderPort
   *            the port of the message sender.
   * @param data
   *            the raw bytes of the message
   * @param len
   *            the length of the byte array.
   * @param tcp
   *            true if the message was received via TCP, false otherwise.
   * @return a SLPMessage of the matching subtype.
   * @throws ServiceLocationException
   *             in case of any parsing errors.
   */
  static SLPMessage parse(final InetAddress senderAddr, final int senderPort,
      final DataInputStream in, final boolean tcp)
      throws ServiceLocationException, ProtocolException {
    try {
      final int version = in.readByte(); // version
      if (version != VERSION) {
        in.readByte(); // funcID
        final int length = in.readShort();
        byte[] drop = new byte[length - 4];
        in.readFully(drop);
        SLPCore.platform.logWarning("Dropped SLPv" + version + " message from "
              + senderAddr + ":" + senderPort);
      }
      final byte funcID = in.readByte(); // funcID
      final int length = readInt(in, 3);

      // slpFlags
      final byte flags = (byte) (in.readShort() >> 8);

      if (!tcp && (flags & 0x80) != 0) {
        throw new ProtocolException();
      }

      // we don't process extensions, we simply ignore them
      readInt(in, 3); // extOffset
      final short xid = in.readShort(); // XID
      final Locale locale = new Locale(in.readUTF(), ""); // Locale

      final SLPMessage msg;

      // decide on the type of the message
      switch (funcID) {
      case DAADVERT:
        msg = new DAAdvertisement(in);
        break;
      case SRVRQST:
        msg = new ServiceRequest(in);
        break;
      case SRVRPLY:
        msg = new ServiceReply(in);
        break;
      case ATTRRQST:
        msg = new AttributeRequest(in);
        break;
      case ATTRRPLY:
        msg = new AttributeReply(in);
        break;
      case SRVREG:
        msg = new ServiceRegistration(in);
        break;
      case SRVDEREG:
        msg = new ServiceDeregistration(in);
        break;
      case SRVACK:
        msg = new ServiceAcknowledgement(in);
        break;
      case SRVTYPERQST:
        msg = new ServiceTypeRequest(in);
        break;
      case SRVTYPERPLY:
        msg = new ServiceTypeReply(in);
        break;
      default:
        throw new ServiceLocationException(
            ServiceLocationException.PARSE_ERROR, "Message type "
                + getType(funcID) + " not supported");
      }

      // set the fields
      msg.address = senderAddr;
      msg.port = senderPort;
      msg.tcp = tcp;
      msg.multicast = ((flags & 0x2000) >> 13) == 1 ? true : false;
      msg.xid = xid;
      msg.funcID = funcID;
      msg.locale = locale;
      if (msg.getSize() != length) {
        SLPCore.platform.logError("Length of " + msg + " should be " + length + ", read "
                + msg.getSize());
//        throw new ServiceLocationException(
//            ServiceLocationException.INTERNAL_SYSTEM_ERROR,
//            "Length of " + msg + " should be " + length + ", read "
//                + msg.getSize());
      }
      return msg;
    } catch (ProtocolException pe) {
      throw pe;
    } catch (IOException ioe) {
      SLPCore.platform.logError("Network Error", ioe);
      throw new ServiceLocationException(
          ServiceLocationException.NETWORK_ERROR, ioe.getMessage());
    }
  }

  /**
   *
   * @return
   */
  protected int getHeaderSize() {
    return 14 + locale.getLanguage().length();
  }

  /**
   *
   * @return
   */
  protected abstract int getSize();

  /**
   * Get a string representation of the message. Overridden by message
   * subtypes.
   *
   * @return a String.
   */
  public String toString() {
    final StringBuffer buffer = new StringBuffer();
    buffer.append(getType(funcID) + " - ");
    buffer.append("xid=" + xid);
    buffer.append(", locale=" + locale);
    return buffer.toString();
  }

  /**
   * returns the string value of the message type, catches the case where an
   * unsupported message has been received.
   *
   * @param type
   *            the type.
   * @return the type as String.
   */
  static String getType(final int type) {
    if (type > -1 && type < 12) {
      return TYPES[type];
    }
    return String.valueOf(type + " - UNSUPPORTED");
  }

  /**
   * parse a numerical value that can be spanned over multiple bytes.
   *
   * @param input
   *            the data input stream.
   * @param len
   *            the number of bytes to read.
   * @return the int value.
   * @throws ServiceLocationException
   *             in case of IO errors.
   */
  private static int readInt(final DataInputStream input, final int len)
      throws ServiceLocationException {
    try {
      int value = 0;
      for (int i = 0; i < len; i++) {
        value <<= 8;
        value += input.readByte() & 0xff;
      }
      return value;
    } catch (IOException ioe) {
      throw new ServiceLocationException(
          ServiceLocationException.PARSE_ERROR, ioe.getMessage());
    }
  }

  /**
   * transforms a Java list to string list.
   *
   * @param list
   *            the list
   * @param delim
   *            the delimiter
   * @return the String list.
   */
  static String listToString(final List list, final String delim) {
    if (list == null || list.size() == 0) {
      return "";
    } else if (list.size() == 1) {
      return list.get(0).toString();
    } else {
      final StringBuffer buffer = new StringBuffer();
      final Object[] elements = list.toArray();
      for (int i = 0; i < elements.length - 1; i++) {
        buffer.append(elements[i]);
        buffer.append(delim);
      }
      buffer.append(elements[elements.length - 1]);
      return buffer.toString();
    }
  }

  /**
   * transforms a string list to Java List.
   *
   * @param str
   *            the String list
   * @param delim
   *            the delimiter
   * @return the List.
   */
  static List stringToList(final String str, final String delim) {
    List result = new ArrayList();
    StringTokenizer tokenizer = new StringTokenizer(str, delim);
    while (tokenizer.hasMoreTokens()) {
      result.add(tokenizer.nextToken());
    }
    return result;
  }

  /**
   *
   * @param input
   * @return
   * @throws ServiceLocationException
   *
   * @author Markus Alexander Kuppe
   * @since 1.1
   */
  protected List attributeStringToList(String input) throws ServiceLocationException {
    if("".equals(input)) {
      return new ArrayList();
    }
    Parser parser = new Parser();
    try {
      Rule parse = parser.parse("attr-list", input);
      AttributeListVisitor visitor = new AttributeListVisitor();
      parse.visit(visitor);
      return visitor.getAttributes();
    } catch (IllegalArgumentException e) {
      throw new ServiceLocationException(ServiceLocationException.PARSE_ERROR, e.getMessage());
    } catch (ParserException e) {
      throw new ServiceLocationException(ServiceLocationException.PARSE_ERROR, e.getMessage());
    }
  }

  /**
   *
   * @param input
   * @return
   * @throws ServiceLocationException
   *
   * @author Markus Alexander Kuppe
   * @since 1.1
   */
  protected List attributeStringToListLiberal(String input) {
    if("".equals(input)) {
      return new ArrayList();
    }
    Parser parser = new Parser();
    Rule rule = null;
    try {
      rule = parser.parse("attr-list", input);
    } catch (IllegalArgumentException e) {
      SLPCore.platform.logError(e.getMessage(), e);
      return new ArrayList();
      // may never happen!!!
    } catch (ParserException e) {
      SLPCore.platform.logTraceDrop(e.getMessage());
      rule = e.getRule();
    }
    AttributeListVisitor visitor = new AttributeListVisitor();
    rule.visit(visitor);
    return visitor.getAttributes();
  }
}
TOP

Related Classes of ch.ethz.iks.slp.impl.SLPMessage

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.