/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
*/
// SrvLocHeader.java: Abstract superclass for SLP Headers
// Author: James Kempf
// Created On: Mon Sep 14 12:47:20 1998
// Last Modified By: James Kempf
// Last Modified On: Mon Nov 23 14:32:50 1998
// Update Count: 55
//
package com.sun.slp;
import java.util.*;
import java.net.*;
import java.io.*;
/**
* SrvLocHeader handles different versions of the SLP header. Clients
* call the instance methods returned by newInstance(). If no version
* specific subclass exists for the version number, null is returned
* from newInstance. Parsing of the header and message bodies, and
* creation of error replies are handled by the version specific
* subclasses. We also let the SrvLocHeader serve as a SrvLocMsg object
* to handle the SrvAck, which only has an error code.
*
* @author James Kempf
*/
abstract class SrvLocHeader extends Object implements SrvLocMsg, Cloneable {
// Table of header classes. Keys are the version number.
private static final Hashtable classTable = new Hashtable();
// Offset to XID.
static final int XID_OFFSET = 10;
// Common constants and instance variables.
// Number of bytes in the version and function fields.
static int VERSION_FUNCTION_BYTES = 2;
// SLP function codes. Even though SAAdvert isn't in all versions,
// we include it here.
static final int SrvReq = 1;
static final int SrvRply = 2;
static final int SrvReg = 3;
static final int SrvDereg = 4;
static final int SrvAck = 5;
static final int AttrRqst = 6;
static final int AttrRply = 7;
static final int DAAdvert = 8;
static final int SrvTypeRqst = 9;
static final int SrvTypeRply = 10;
static final int SAAdvert = 11;
static final String[] functionCodeAbbr = {
"0",
"SrvReq",
"SrvRply",
"SrvReg",
"SrvDereg",
"SrvAck",
"AttrRqst",
"AttrRply",
"DAAdvert",
"SrvTypeRqst",
"SrvTypeRply",
"SAAdvert",
};
// Sizes of data items.
protected static final int BYTE_SIZE = 1;
protected static final int SHORT_SIZE = 2;
protected static final int INT24_SIZE = 3;
//
// Header instance variables.
//
// Unprotected for less code.
int version = 0; // version number
int functionCode = 0; // function code
int length = 0; // packet length
short xid = 0; // transaction id
short errCode =
ServiceLocationException.OK; // not applicable to start
Locale locale = Defaults.locale; // language locale
Vector previousResponders = null; // list of previous responders
Vector scopes = null; // list of scopes
boolean overflow = false; // Overflow flag
boolean fresh = false; // Fresh flag
boolean mcast = false; // Mulitcast flag.
byte[] payload = new byte[0]; // bytes of outgoing payload,
int nbytes = 0; // number of bytes processed
int packetLength = 0; // length of packet.
int iNumReplies = 0; // number of replies.
protected static short uniqueXID = 0; // outgoing transaction id.
// Message description.
private String msgType;
private String msgDescription;
SrvLocHeader() {
packetLength = SLPConfig.getSLPConfig().getMTU();
}
//
// SrvLocMsg Implementation.
//
public SrvLocHeader getHeader() {
return this;
}
public short getErrorCode() {
return errCode;
}
// Return number of replies to this message.
public int getNumReplies() {
return iNumReplies;
}
//
// SrvLocHeader Interface.
//
// Register a new header class for version. Serious error, causing
// program termination, if we can't find it.
static void addHeaderClass(String className, int version) {
try {
Class headerClass = Class.forName(className);
classTable.put(new Integer(version), headerClass);
} catch (ClassNotFoundException ex) {
Assert.slpassert(false,
"no_class",
new Object[] {className});
}
}
// Create a version specific instance. We use a naming convention
// to identify the version specific classes used to create the
// instance.
static SrvLocHeader newInstance(int version) {
try {
// Get header class.
Class hdrClass = (Class)classTable.get(new Integer(version));
if (hdrClass == null) {
return null;
}
SrvLocHeader hdr = (SrvLocHeader)hdrClass.newInstance();
return hdr;
} catch (Exception ex) {
SLPConfig.getSLPConfig().writeLog("slh_creation_exception",
new Object[] {
new Integer(version),
ex,
ex.getMessage()});
return null;
}
}
// Parse the incoming stream to obtain the header.
abstract void parseHeader(int functionCode, DataInputStream dis)
throws ServiceLocationException, IOException, IllegalArgumentException;
// Parse the incoming stream to obtain the message.
abstract SrvLocMsg parseMsg(DataInputStream dis)
throws ServiceLocationException, IOException, IllegalArgumentException;
// Externalize the message.
abstract void
externalize(ByteArrayOutputStream baos,
boolean multicast,
boolean isTCP)
throws ServiceLocationException;
// Return the appropriately versioned DAAdvert.
abstract SDAAdvert
getDAAdvert(short xid,
long timestamp,
ServiceURL url,
Vector scopes,
Vector attrs)
throws ServiceLocationException;
//
// Methods that some subclasses may reimplement.
//
// Parse any options.
void parseOptions(DataInputStream dis)
throws ServiceLocationException,
IOException,
IllegalArgumentException {
}
// Create an error reply for this message. This reply will be appropriate
// for the server to send back to the client. Default is to do nothing,
// which is the code for the client.
SrvLocMsg makeErrorReply(Exception ex) {
return null;
}
//
// Common utilities for all versions.
//
// Set the packet length to the incoming value.
void setPacketLength(int newLength) {
if (newLength > 0) {
packetLength = newLength;
}
}
// Add an Internet address to the previous responders list.
void addPreviousResponder(InetAddress addr) {
String hostAddr = addr.getHostAddress();
Assert.slpassert((previousResponders != null),
"prev_resp_reply",
new Object[0]);
if (!previousResponders.contains(hostAddr)) {
previousResponders.addElement(hostAddr);
}
}
// Get a unique transaction id.
synchronized static short getUniqueXID() {
if (uniqueXID == 0) {
Random r = new Random();
uniqueXID = (short)(r.nextInt() &0xFFFF);
}
uniqueXID++;
return (short)(uniqueXID & 0xFFFF);
}
// Parse 2-byte integer, bump byte count.
int getInt(DataInputStream dis)
throws ServiceLocationException, IOException {
int ret = getInteger(dis);
nbytes += SHORT_SIZE;
return ret;
}
// Parse a 2-byte integer from the input stream.
static int getInteger(DataInputStream dis)
throws ServiceLocationException, IOException {
byte[] b = new byte[2];
dis.readFully(b, 0, 2);
int x = (int)((char)b[0] & 0xFF);
int y = (int)((char)b[1] & 0xFF);
int z = x << 8;
z += y;
return z;
}
// Parse 2-byte integer, bump byte count.
void putInt(int z, ByteArrayOutputStream baos) {
putInteger(z, baos);
nbytes += SHORT_SIZE;
}
// Parse a 2-byte integer to the output stream.
static void putInteger(int z, ByteArrayOutputStream baos) {
baos.write((byte) ((0xFF00 & z)>>8));
baos.write((byte) (0xFF & z));
}
// Parse a 3-byte integer from the byte input stream.
protected int getInt24(DataInputStream dis)
throws ServiceLocationException, IOException {
byte[] b = new byte[3];
dis.readFully(b, 0, 3);
int w = (int)((char)b[0] & 0xFF);
int x = (int)((char)b[1] & 0xFF);
int y = (int)((char)b[2] & 0xFF);
int z = w << 16;
z += x << 8;
z += y;
nbytes += 3;
return z;
}
// Parse a 3-byte integer to the output stream.
protected void putInt24(int z, ByteArrayOutputStream baos) {
baos.write((byte) ((0xFF0000 & z) >> 16));
baos.write((byte) ((0xFF00 & z)>>8));
baos.write((byte) (0xFF & z));
nbytes += 3;
}
// Parse string, bump byte count. Use UTF8 encoding.
byte[] getString(StringBuffer buf, DataInputStream dis)
throws ServiceLocationException, IOException {
byte[] ret = getStringField(buf, dis, Defaults.UTF8);
nbytes += ret.length + SHORT_SIZE;
return ret;
}
// Parse a string with an initial length from the input stream.
// Convert it to the proper encoding. Return the raw bytes for
// auth block creation.
static byte[]
getStringField(StringBuffer buf, DataInputStream dis, String encoding)
throws ServiceLocationException, IOException {
// Clear out buffer first.
buf.setLength(0);
// First get the length.
int i, n = 0;
n = getInteger(dis);
// Now get the bytes.
byte[] bytes = new byte[n];
dis.readFully(bytes, 0, n);
// Convert to string and return.
buf.append(getBytesString(bytes, encoding));
return bytes;
}
// Parse out string, bump byte count. Use UTF8 encoding.
byte[] putString(String string, ByteArrayOutputStream baos) {
byte[] bytes = putStringField(string, baos, Defaults.UTF8);
nbytes += bytes.length + SHORT_SIZE;
return bytes;
}
// Put a string with an initial length into the byte stream, converting
// into the proper encoding.
static byte[]
putStringField(String string,
ByteArrayOutputStream baos,
String encoding) {
byte[] bytes = getStringBytes(string, encoding);
// Put out the string's length in the encoding.
putInteger(bytes.length, baos);
// Now really write out the bytes.
baos.write(bytes, 0, bytes.length);
return bytes;
}
// Convert a Unicode string into encoded bytes.
static byte[] getStringBytes(String string, String encoding) {
try {
return string.getBytes(encoding);
} catch (UnsupportedEncodingException ex) {
return new byte[0]; // won't happen, hopefully...
}
}
// Convert bytes into a Unicode string.
static String getBytesString(byte[] bytes, String encoding) {
try {
return new String(bytes, encoding);
} catch (UnsupportedEncodingException ex) {
return ""; // won't happen, hopefully ...
}
}
// Parse a comma separated list of strings from the vector into the
// output stream.
protected byte[]
parseCommaSeparatedListOut(Vector v,
ByteArrayOutputStream baos) {
return putString(vectorToCommaSeparatedList(v), baos);
}
/**
* Create a comma separated list of strings out of the vector.
*
* @param v A Vector of strings.
*/
static String
vectorToCommaSeparatedList(Vector v) {
// Construct in a string buffer first.
int i, n = v.size();
StringBuffer buf = new StringBuffer();
for (i = 0; i < n; i++) {
String string = (String)v.elementAt(i);
// Add comma for previous one if we need it.
if (i != 0) {
buf.append(',');
}
buf.append(string);
}
// Return the bytes.
return buf.toString();
}
/**
* @parameter The string has the format = STRING *("," STRING)
* @parameter A boolean indicating whether parens should be ignored or
* used for grouping.
* @return A vector (of Strings) based upon the (comma delimited) string.
*/
static Vector parseCommaSeparatedListIn(String s, boolean ignoreParens)
throws ServiceLocationException {
if (s == null)
return new Vector();
if (s.length() == 0)
return new Vector();
StringTokenizer st = new StringTokenizer(s, ",()", true);
try {
int level = 0;
String el = "";
Vector v = new Vector();
while (st.hasMoreElements()) {
String tok = st.nextToken();
// It's an open paren, so begin collecting.
if (tok.equals("(")) {
// Increment the level if not ignoring parens, add to token
if (!ignoreParens) {
level++;
}
el += tok;
} else if (tok.equals(")")) {
// Decrement level if not ignoring parens.
if (!ignoreParens) {
level--;
}
el += tok;
} else if (tok.equals(",")) {
// Add if collecting.
if (level != 0) {
el += tok;
} else {
// Check for empty token.
if (el.length() <= 0) {
throw
new ServiceLocationException(
ServiceLocationException.PARSE_ERROR,
"csl_syntax_error",
new Object[] {s});
}
// If not collecting, then close off the element.
v.addElement(el);
el = "";
}
} else {
el += tok;
}
}
// Add last token, but check first for empty token.
if (el.length() <= 0) {
throw
new ServiceLocationException(
ServiceLocationException.PARSE_ERROR,
"csl_syntax_error",
new Object[] {s});
}
v.addElement(el);
// If we're still collecting on close, then there's a syntax error.
if (level != 0) {
throw
new ServiceLocationException(
ServiceLocationException.PARSE_ERROR,
"csl_syntax_error",
new Object[] {s});
}
return v;
} catch (NoSuchElementException nsee) {
throw
new ServiceLocationException(
ServiceLocationException.PARSE_ERROR,
"csl_syntax_error",
new Object[] {s});
}
}
// Allow clients to clone the header.
public Object clone()
throws CloneNotSupportedException {
SrvLocHeader hdr = (SrvLocHeader)super.clone();
// Reinitialize some stuff. Subclasses must reimplement nbytes
// header size calculation.
hdr.length = 0;
hdr.payload = new byte[0];
hdr.iNumReplies = 0;
// packetlength stays the same, we may be using the same transport.
return hdr;
}
// Construct a description of the header. Messages add individual
// descriptions to this.
protected void constructDescription(String msgType,
String msgDescription) {
this.msgType = msgType;
this.msgDescription = msgDescription;
}
public String getMsgType() {
if (msgType == null) {
if (functionCode > 0 && functionCode < functionCodeAbbr.length) {
return functionCodeAbbr[functionCode];
} else {
return String.valueOf(functionCode);
}
} else {
return msgType;
}
}
public String getMsgDescription() {
return (msgDescription == null) ? "" : msgDescription;
}
}