tomcat-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cos...@apache.org
Subject svn commit: r640706 [6/12] - in /tomcat/sandbox/tomcat-lite/tomcat-coyote: ./ org/ org/apache/ org/apache/coyote/ org/apache/coyote/http11/ org/apache/coyote/http11/filters/ org/apache/juli/ org/apache/juli/logging/ org/apache/tomcat/ org/apache/tomcat...
Date Tue, 25 Mar 2008 06:01:57 GMT
Added: tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/collections/MultiMapNamesEnumeration.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/collections/MultiMapNamesEnumeration.java?rev=640706&view=auto
==============================================================================
--- tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/collections/MultiMapNamesEnumeration.java (added)
+++ tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/collections/MultiMapNamesEnumeration.java Mon Mar 24 23:01:43 2008
@@ -0,0 +1,81 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.collections;
+
+import java.util.Enumeration;
+
+/** Enumerate the distinct header names.
+    Each nextElement() is O(n) ( a comparation is
+    done with all previous elements ).
+
+    This is less frequesnt than add() -
+    we want to keep add O(1).
+*/
+public final class MultiMapNamesEnumeration implements Enumeration {
+    int pos;
+    int size;
+    String next;
+    MultiMap headers;
+
+    // toString and unique options are not implemented -
+    // we allways to toString and unique.
+    
+    /** Create a new multi-map enumeration.
+     * @param  headers the collection to enumerate 
+     * @param  toString convert each name to string 
+     * @param  unique return only unique names
+     */
+    MultiMapNamesEnumeration(MultiMap headers, boolean toString,
+			     boolean unique) {
+	this.headers=headers;
+	pos=0;
+	size = headers.size();
+	findNext();
+    }
+
+    private void findNext() {
+	next=null;
+	for(  ; pos< size; pos++ ) {
+	    next=headers.getName( pos ).toString();
+	    for( int j=0; j<pos ; j++ ) {
+		if( headers.getName( j ).equalsIgnoreCase( next )) {
+		    // duplicate.
+		    next=null;
+		    break;
+		}
+	    }
+	    if( next!=null ) {
+		// it's not a duplicate
+		break;
+	    }
+	}
+	// next time findNext is called it will try the
+	// next element
+	pos++;
+    }
+    
+    public boolean hasMoreElements() {
+	return next!=null;
+    }
+
+    public Object nextElement() {
+	String current=next;
+	findNext();
+	return current;
+    }
+}

Added: tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/collections/MultiMapValuesEnumeration.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/collections/MultiMapValuesEnumeration.java?rev=640706&view=auto
==============================================================================
--- tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/collections/MultiMapValuesEnumeration.java (added)
+++ tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/collections/MultiMapValuesEnumeration.java Mon Mar 24 23:01:43 2008
@@ -0,0 +1,64 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.collections;
+
+import java.util.Enumeration;
+
+import org.apache.tomcat.util.buf.MessageBytes;
+
+/** Enumerate the values for a (possibly ) multiple
+ *    value element.
+ */
+class MultiMapValuesEnumeration implements Enumeration {
+    int pos;
+    int size;
+    MessageBytes next;
+    MultiMap headers;
+    String name;
+
+    MultiMapValuesEnumeration(MultiMap headers, String name,
+			      boolean toString) {
+        this.name=name;
+	this.headers=headers;
+	pos=0;
+	size = headers.size();
+	findNext();
+    }
+
+    private void findNext() {
+	next=null;
+	for( ; pos< size; pos++ ) {
+	    MessageBytes n1=headers.getName( pos );
+	    if( n1.equalsIgnoreCase( name )) {
+		next=headers.getValue( pos );
+		break;
+	    }
+	}
+	pos++;
+    }
+    
+    public boolean hasMoreElements() {
+	return next!=null;
+    }
+
+    public Object nextElement() {
+	MessageBytes current=next;
+	findNext();
+	return current.toString();
+    }
+}

Added: tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/collections/Queue.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/collections/Queue.java?rev=640706&view=auto
==============================================================================
--- tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/collections/Queue.java (added)
+++ tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/collections/Queue.java Mon Mar 24 23:01:43 2008
@@ -0,0 +1,103 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.collections;
+
+import java.util.Vector;
+
+/**
+ * A simple FIFO queue class which causes the calling thread to wait
+ * if the queue is empty and notifies threads that are waiting when it
+ * is not empty.
+ *
+ * @author Anil V (akv@eng.sun.com)
+ */
+public class Queue {
+    private Vector vector = new Vector();
+    private boolean stopWaiting=false;
+    private boolean waiting=false;
+    
+    /** 
+     * Put the object into the queue.
+     * 
+     * @param	object		the object to be appended to the
+     * 				queue. 
+     */
+    public synchronized void put(Object object) {
+	vector.addElement(object);
+	notify();
+    }
+
+    /** Break the pull(), allowing the calling thread to exit
+     */
+    public synchronized void stop() {
+	stopWaiting=true;
+	// just a hack to stop waiting 
+	if( waiting ) notify();
+    }
+    
+    /**
+     * Pull the first object out of the queue. Wait if the queue is
+     * empty.
+     */
+    public synchronized Object pull() {
+	while (isEmpty()) {
+	    try {
+		waiting=true;
+		wait();
+	    } catch (InterruptedException ex) {
+	    }
+	    waiting=false;
+	    if( stopWaiting ) return null;
+	}
+	return get();
+    }
+
+    /**
+     * Get the first object out of the queue. Return null if the queue
+     * is empty. 
+     */
+    public synchronized Object get() {
+	Object object = peek();
+	if (object != null)
+	    vector.removeElementAt(0);
+	return object;
+    }
+
+    /**
+     * Peek to see if something is available.
+     */
+    public Object peek() {
+	if (isEmpty())
+	    return null;
+	return vector.elementAt(0);
+    }
+    
+    /**
+     * Is the queue empty?
+     */
+    public boolean isEmpty() {
+	return vector.isEmpty();
+    }
+
+    /**
+     * How many elements are there in this queue?
+     */
+    public int size() {
+	return vector.size();
+    }
+}

Added: tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/collections/SimpleHashtable.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/collections/SimpleHashtable.java?rev=640706&view=auto
==============================================================================
--- tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/collections/SimpleHashtable.java (added)
+++ tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/collections/SimpleHashtable.java Mon Mar 24 23:01:43 2008
@@ -0,0 +1,324 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.collections;
+
+import java.util.Enumeration;
+
+/* **************************************** Stolen from Crimson ******************** */
+/* From Crimson/Parser - in a perfect world we'll just have a common set of
+   utilities, and all apache project will just use those.
+
+*/
+
+// can't be replaced using a Java 2 "Collections" API
+// since this package must also run on JDK 1.1
+
+
+/**
+ * This class implements a special purpose hashtable.  It works like a
+ * normal <code>java.util.Hashtable</code> except that: <OL>
+ *
+ *	<LI> Keys to "get" are strings which are known to be interned,
+ *	so that "==" is used instead of "String.equals".  (Interning
+ *	could be document-relative instead of global.)
+ *
+ *	<LI> It's not synchronized, since it's to be used only by
+ *	one thread at a time.
+ *
+ *	<LI> The keys () enumerator allocates no memory, with live
+ *	updates to the data disallowed.
+ *
+ *	<LI> It's got fewer bells and whistles:  fixed threshold and
+ *	load factor, no JDK 1.2 collection support, only keys can be
+ *	enumerated, things can't be removed, simpler inheritance; more.
+ *
+ *	</OL>
+ *
+ * <P> The overall result is that it's less expensive to use these in
+ * performance-critical locations, in terms both of CPU and memory,
+ * than <code>java.util.Hashtable</code> instances.  In this package
+ * it makes a significant difference when normalizing attributes,
+ * which is done for each start-element construct.
+ *
+ */
+public final class SimpleHashtable implements Enumeration
+{
+    
+    private static org.apache.juli.logging.Log log=
+        org.apache.juli.logging.LogFactory.getLog( SimpleHashtable.class );
+    
+    // entries ...
+    private Entry		table[];
+
+    // currently enumerated key
+    private Entry		current = null;
+    private int			currentBucket = 0;
+
+    // number of elements in hashtable
+    private int			count;
+    private int			threshold;
+
+    private static final float	loadFactor = 0.75f;
+
+
+    /**
+     * Constructs a new, empty hashtable with the specified initial 
+     * capacity.
+     *
+     * @param      initialCapacity   the initial capacity of the hashtable.
+     */
+    public SimpleHashtable(int initialCapacity) {
+	if (initialCapacity < 0)
+	    throw new IllegalArgumentException("Illegal Capacity: "+
+                                               initialCapacity);
+        if (initialCapacity==0)
+            initialCapacity = 1;
+	table = new Entry[initialCapacity];
+	threshold = (int)(initialCapacity * loadFactor);
+    }
+
+    /**
+     * Constructs a new, empty hashtable with a default capacity.
+     */
+    public SimpleHashtable() {
+	this(11);
+    }
+
+    /**
+     */
+    public void clear ()
+    {
+	count = 0;
+	currentBucket = 0;
+	current = null;
+	for (int i = 0; i < table.length; i++)
+	    table [i] = null;
+    }
+
+    /**
+     * Returns the number of keys in this hashtable.
+     *
+     * @return  the number of keys in this hashtable.
+     */
+    public int size() {
+	return count;
+    }
+
+    /**
+     * Returns an enumeration of the keys in this hashtable.
+     *
+     * @return  an enumeration of the keys in this hashtable.
+     * @see     Enumeration
+     */
+    public Enumeration keys() {
+	currentBucket = 0;
+	current = null;
+	hasMoreElements();
+	return this;
+    }
+
+    /**
+     * Used to view this as an enumeration; returns true if there
+     * are more keys to be enumerated.
+     */
+    public boolean hasMoreElements ()
+    {
+	if (current != null)
+	    return true;
+	while (currentBucket < table.length) {
+	    current = table [currentBucket++];
+	    if (current != null)
+		return true;
+	}
+	return false;
+    }
+
+    /**
+     * Used to view this as an enumeration; returns the next key
+     * in the enumeration.
+     */
+    public Object nextElement ()
+    {
+	Object retval;
+
+	if (current == null)
+	    throw new IllegalStateException ();
+	retval = current.key;
+	current = current.next;
+	// Advance to the next position ( we may call next after next,
+	// without hasMore )
+	hasMoreElements();
+	return retval;
+    }
+
+
+    /**
+     * Returns the value to which the specified key is mapped in this hashtable.
+     */
+    public Object getInterned (String key) {
+	Entry tab[] = table;
+	int hash = key.hashCode();
+	int index = (hash & 0x7FFFFFFF) % tab.length;
+	for (Entry e = tab[index] ; e != null ; e = e.next) {
+	    if ((e.hash == hash) && (e.key == key))
+		return e.value;
+	}
+	return null;
+    }
+
+    /**
+     * Returns the value to which the specified key is mapped in this
+     * hashtable ... the key isn't necessarily interned, though.
+     */
+    public Object get(String key) {
+	Entry tab[] = table;
+	int hash = key.hashCode();
+	int index = (hash & 0x7FFFFFFF) % tab.length;
+	for (Entry e = tab[index] ; e != null ; e = e.next) {
+	    if ((e.hash == hash) && e.key.equals(key))
+		return e.value;
+	}
+	return null;
+    }
+
+    /**
+     * Increases the capacity of and internally reorganizes this 
+     * hashtable, in order to accommodate and access its entries more 
+     * efficiently.  This method is called automatically when the 
+     * number of keys in the hashtable exceeds this hashtable's capacity 
+     * and load factor. 
+     */
+    private void rehash() {
+	int oldCapacity = table.length;
+	Entry oldMap[] = table;
+
+	int newCapacity = oldCapacity * 2 + 1;
+	Entry newMap[] = new Entry[newCapacity];
+
+	threshold = (int)(newCapacity * loadFactor);
+	table = newMap;
+
+	/*
+	System.out.pr intln("rehash old=" + oldCapacity
+		+ ", new=" + newCapacity
+		+ ", thresh=" + threshold
+		+ ", count=" + count);
+	*/
+
+	for (int i = oldCapacity ; i-- > 0 ;) {
+	    for (Entry old = oldMap[i] ; old != null ; ) {
+		Entry e = old;
+		old = old.next;
+
+		int index = (e.hash & 0x7FFFFFFF) % newCapacity;
+		e.next = newMap[index];
+		newMap[index] = e;
+	    }
+	}
+    }
+
+    /**
+     * Maps the specified <code>key</code> to the specified 
+     * <code>value</code> in this hashtable. Neither the key nor the 
+     * value can be <code>null</code>. 
+     *
+     * <P>The value can be retrieved by calling the <code>get</code> method 
+     * with a key that is equal to the original key. 
+     */
+    public Object put(Object key, Object value) {
+	// Make sure the value is not null
+	if (value == null) {
+	    throw new NullPointerException();
+	}
+
+	// Makes sure the key is not already in the hashtable.
+	Entry tab[] = table;
+	int hash = key.hashCode();
+	int index = (hash & 0x7FFFFFFF) % tab.length;
+	for (Entry e = tab[index] ; e != null ; e = e.next) {
+	    // if ((e.hash == hash) && e.key.equals(key)) {
+	    if ((e.hash == hash) && (e.key == key)) {
+		Object old = e.value;
+		e.value = value;
+		return old;
+	    }
+	}
+
+	if (count >= threshold) {
+	    // Rehash the table if the threshold is exceeded
+	    rehash();
+
+            tab = table;
+            index = (hash & 0x7FFFFFFF) % tab.length;
+	} 
+
+	// Creates the new entry.
+	Entry e = new Entry(hash, key, value, tab[index]);
+	tab[index] = e;
+	count++;
+	return null;
+    }
+
+    public Object remove(Object key) {
+	Entry tab[] = table;
+	Entry prev=null;
+	int hash = key.hashCode();
+	int index = (hash & 0x7FFFFFFF) % tab.length;
+	if( dL > 0 ) d("Idx " + index +  " " + tab[index] );
+	for (Entry e = tab[index] ; e != null ; prev=e, e = e.next) {
+	    if( dL > 0 ) d("> " + prev + " " + e.next + " " + e + " " + e.key);
+	    if ((e.hash == hash) && e.key.equals(key)) {
+		if( prev!=null ) {
+		    prev.next=e.next;
+		} else {
+		    tab[index]=e.next;
+		}
+		if( dL > 0 ) d("Removing from list " + tab[index] + " " + prev +
+			       " " + e.value);
+		count--;
+		Object res=e.value;
+		e.value=null;
+		return res;
+	    }
+	}
+	return null;
+    }
+
+    /**
+     * Hashtable collision list.
+     */
+    private static class Entry {
+	int	hash;
+	Object	key;
+	Object	value;
+	Entry	next;
+
+	protected Entry(int hash, Object key, Object value, Entry next) {
+	    this.hash = hash;
+	    this.key = key;
+	    this.value = value;
+	    this.next = next;
+	}
+    }
+
+    private static final int dL=0;
+    private void d(String s ) {
+	if (log.isDebugEnabled())
+            log.debug( "SimpleHashtable: " + s );
+    }
+}

Added: tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/collections/SimplePool.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/collections/SimplePool.java?rev=640706&view=auto
==============================================================================
--- tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/collections/SimplePool.java (added)
+++ tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/collections/SimplePool.java Mon Mar 24 23:01:43 2008
@@ -0,0 +1,128 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.collections;
+
+/**
+ * Simple object pool. Based on ThreadPool and few other classes
+ *
+ * The pool will ignore overflow and return null if empty.
+ *
+ * @author Gal Shachor
+ * @author Costin Manolache
+ */
+public final class SimplePool  {
+    
+    
+    private static org.apache.juli.logging.Log log=
+        org.apache.juli.logging.LogFactory.getLog(SimplePool.class );
+    
+    /*
+     * Where the threads are held.
+     */
+    private Object pool[];
+
+    private int max;
+    private int last;
+    private int current=-1;
+    
+    private Object lock;
+    public static final int DEFAULT_SIZE=32;
+    static final int debug=0;
+    
+    public SimplePool() {
+	this(DEFAULT_SIZE,DEFAULT_SIZE);
+    }
+
+    public SimplePool(int size) {
+	this(size, size);
+    }
+
+    public SimplePool(int size, int max) {
+	this.max=max;
+	pool=new Object[size];
+	this.last=size-1;
+	lock=new Object();
+    }
+
+    public  void set(Object o) {
+	put(o);
+    }
+
+    /**
+     * Add the object to the pool, silent nothing if the pool is full
+     */
+    public  void put(Object o) {
+	synchronized( lock ) {
+	    if( current < last ) {
+		current++;
+		pool[current] = o;
+            } else if( current < max ) {
+		// realocate
+		int newSize=pool.length*2;
+		if( newSize > max ) newSize=max+1;
+		Object tmp[]=new Object[newSize];
+		last=newSize-1;
+		System.arraycopy( pool, 0, tmp, 0, pool.length);
+		pool=tmp;
+		current++;
+		pool[current] = o;
+	    }
+	    if( debug > 0 ) log("put " + o + " " + current + " " + max );
+	}
+    }
+
+    /**
+     * Get an object from the pool, null if the pool is empty.
+     */
+    public  Object get() {
+	Object item = null;
+	synchronized( lock ) {
+	    if( current >= 0 ) {
+		item = pool[current];
+		pool[current] = null;
+		current -= 1;
+	    }
+	    if( debug > 0 ) 
+		log("get " + item + " " + current + " " + max);
+	}
+	return item;
+    }
+
+    /**
+     * Return the size of the pool
+     */
+    public int getMax() {
+	return max;
+    }
+
+    /**
+     * Number of object in the pool
+     */
+    public int getCount() {
+	return current+1;
+    }
+
+
+    public void shutdown() {
+    }
+    
+    private void log( String s ) {
+        if (log.isDebugEnabled())
+            log.debug("SimplePool: " + s );
+    }
+}

Added: tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/collections/package.html
URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/collections/package.html?rev=640706&view=auto
==============================================================================
--- tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/collections/package.html (added)
+++ tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/collections/package.html Mon Mar 24 23:01:43 2008
@@ -0,0 +1,33 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<html>
+<head>
+<title>util.collections</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<h1>Specialized collections</h1>
+
+This package includes a number of special collections, tunned for server-side
+applications. 
+
+The utils are not tomcat specific, but use MessageBytes and few other 
+top-level utils.
+
+</body>
+</html>

Added: tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/http/AcceptLanguage.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/http/AcceptLanguage.java?rev=640706&view=auto
==============================================================================
--- tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/http/AcceptLanguage.java (added)
+++ tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/http/AcceptLanguage.java Mon Mar 24 23:01:43 2008
@@ -0,0 +1,151 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.http;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Locale;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+/**
+ * Util to process the "Accept-Language" header. Used by facade to implement
+ * getLocale() and by StaticInterceptor.
+ *
+ * Not optimized - it's very slow.
+ * 
+ * @author James Duncan Davidson [duncan@eng.sun.com]
+ * @author James Todd [gonzo@eng.sun.com]
+ * @author Jason Hunter [jch@eng.sun.com]
+ * @author Harish Prabandham
+ * @author costin@eng.sun.com
+ */
+public class AcceptLanguage {
+
+    public static Locale getLocale(String acceptLanguage) {
+        if( acceptLanguage == null ) return Locale.getDefault();
+
+        Hashtable<String,Vector<String>> languages =
+            new Hashtable<String,Vector<String>>();
+        Vector<Double> quality = new Vector<Double>();
+        processAcceptLanguage(acceptLanguage, languages, quality);
+
+        if (languages.size() == 0) return Locale.getDefault();
+
+        Vector<Locale> l = new Vector<Locale>();
+        extractLocales( languages,quality, l);
+
+        return (Locale)l.elementAt(0);
+    }
+
+    public static Enumeration getLocales(String acceptLanguage) {
+            // Short circuit with an empty enumeration if null header
+        if (acceptLanguage == null) {
+            Vector<Locale> v = new Vector<Locale>();
+            v.addElement(Locale.getDefault());
+            return v.elements();
+        }
+        
+        Hashtable<String,Vector<String>> languages =
+            new Hashtable<String,Vector<String>>();
+        Vector<Double> quality=new Vector<Double>();
+            processAcceptLanguage(acceptLanguage, languages , quality);
+
+        if (languages.size() == 0) {
+            Vector<Locale> v = new Vector<Locale>();
+            v.addElement(Locale.getDefault());
+            return v.elements();
+        }
+            Vector<Locale> l = new Vector<Locale>();
+            extractLocales( languages, quality , l);
+            return l.elements();
+    }
+
+    private static void processAcceptLanguage( String acceptLanguage,
+            Hashtable<String,Vector<String>> languages, Vector<Double> q)
+    {
+        StringTokenizer languageTokenizer =
+            new StringTokenizer(acceptLanguage, ",");
+
+        while (languageTokenizer.hasMoreTokens()) {
+            String language = languageTokenizer.nextToken().trim();
+            int qValueIndex = language.indexOf(';');
+            int qIndex = language.indexOf('q');
+            int equalIndex = language.indexOf('=');
+            Double qValue = new Double(1);
+
+            if (qValueIndex > -1 &&
+                    qValueIndex < qIndex &&
+                    qIndex < equalIndex) {
+                    String qValueStr = language.substring(qValueIndex + 1);
+                language = language.substring(0, qValueIndex);
+                qValueStr = qValueStr.trim().toLowerCase();
+                qValueIndex = qValueStr.indexOf('=');
+                qValue = new Double(0);
+                if (qValueStr.startsWith("q") &&
+                    qValueIndex > -1) {
+                    qValueStr = qValueStr.substring(qValueIndex + 1);
+                    try {
+                        qValue = new Double(qValueStr.trim());
+                    } catch (NumberFormatException nfe) {
+                    }
+                }
+            }
+
+            // XXX
+            // may need to handle "*" at some point in time
+
+            if (! language.equals("*")) {
+                String key = qValue.toString();
+                Vector<String> v;
+                if (languages.containsKey(key)) {
+                    v = languages.get(key) ;
+                } else {
+                    v= new Vector<String>();
+                    q.addElement(qValue);
+                }
+                v.addElement(language);
+                languages.put(key, v);
+            }
+        }
+    }
+
+    private static void extractLocales(Hashtable languages, Vector q,
+            Vector<Locale> l)
+    {
+        // XXX We will need to order by q value Vector in the Future ?
+        Enumeration e = q.elements();
+        while (e.hasMoreElements()) {
+            Vector v =
+                (Vector)languages.get(((Double)e.nextElement()).toString());
+            Enumeration le = v.elements();
+            while (le.hasMoreElements()) {
+                    String language = (String)le.nextElement();
+                        String country = "";
+                        int countryIndex = language.indexOf("-");
+                if (countryIndex > -1) {
+                    country = language.substring(countryIndex + 1).trim();
+                    language = language.substring(0, countryIndex).trim();
+                }
+                l.addElement(new Locale(language, country));
+            }
+        }
+    }
+
+
+}

Added: tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/http/BaseRequest.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/http/BaseRequest.java?rev=640706&view=auto
==============================================================================
--- tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/http/BaseRequest.java (added)
+++ tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/http/BaseRequest.java Mon Mar 24 23:01:43 2008
@@ -0,0 +1,346 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Base http request object.                                  *
+ * Author:      Keving Seguin [seguin@apache.org]                          *
+ * Version:     $Revision: 553700 $                                           *
+ ***************************************************************************/
+
+package org.apache.tomcat.util.http;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import java.util.HashMap;
+import java.util.Iterator;
+
+import org.apache.tomcat.util.buf.MessageBytes;
+
+/**
+ * A general-purpose object for representing an HTTP
+ * request.
+ */
+public class BaseRequest {
+
+    // scheme constants
+    public static final String SCHEME_HTTP = "http";
+    public static final String SCHEME_HTTPS = "https";
+
+    // request attributes
+    MessageBytes method = MessageBytes.newInstance();
+    MessageBytes protocol = MessageBytes.newInstance();
+    MessageBytes requestURI = MessageBytes.newInstance();
+    MessageBytes remoteAddr = MessageBytes.newInstance();
+    MessageBytes remoteHost = MessageBytes.newInstance();
+    MessageBytes serverName = MessageBytes.newInstance();
+    int serverPort = 80;
+    MessageBytes remoteUser = MessageBytes.newInstance();
+    MessageBytes authType = MessageBytes.newInstance();
+    MessageBytes queryString = MessageBytes.newInstance();
+    MessageBytes authorization = MessageBytes.newInstance();
+    String scheme = SCHEME_HTTP;
+    boolean secure = false;
+    int contentLength = 0;
+    MessageBytes contentType = MessageBytes.newInstance();
+    MimeHeaders headers = new MimeHeaders();
+    Cookies cookies = new Cookies();
+    HashMap<String,Object> attributes = new HashMap<String,Object>();
+
+    MessageBytes tomcatInstanceId = MessageBytes.newInstance();
+    
+    /**
+     * Recycles this object and readies it further use.
+     */
+    public void recycle() {
+        method.recycle();
+        protocol.recycle();
+        requestURI.recycle();
+        remoteAddr.recycle();
+        remoteHost.recycle();
+        serverName.recycle();
+        serverPort = 80;
+        remoteUser.recycle();
+        authType.recycle();
+        queryString.recycle();
+        authorization.recycle();
+        scheme = SCHEME_HTTP;
+        secure = false;
+        contentLength = 0;
+        contentType.recycle();
+        headers.recycle();
+        cookies.recycle();
+        attributes.clear();
+        tomcatInstanceId.recycle();
+    }
+
+    /**
+     * Get the method.
+     * @return the method
+     */
+    public MessageBytes method() {
+        return method;
+    }
+
+    /**
+     * Get the protocol
+     * @return the protocol
+     */
+    public MessageBytes protocol() {
+        return protocol;
+    }
+
+    /**
+     * Get the request uri
+     * @return the request uri
+     */
+    public MessageBytes requestURI() {
+        return requestURI;
+    }
+
+    /**
+     * Get the remote address
+     * @return the remote address
+     */
+    public MessageBytes remoteAddr() {
+        return remoteAddr;
+    }
+
+    /**
+     * Get the remote host
+     * @return the remote host
+     */
+    public MessageBytes remoteHost() {
+        return remoteHost;
+    }
+
+    /**
+     * Get the server name
+     * @return the server name
+     */
+    public MessageBytes serverName() {
+        return serverName;
+    }
+
+    /**
+     * Get the server port
+     * @return the server port
+     */
+    public int getServerPort() {
+        return serverPort;
+    }
+
+    /**
+     * Set the server port
+     * @param i the server port
+     */
+    public void setServerPort(int i) {
+        serverPort = i;
+    }
+
+    /**
+     * Get the remote user
+     * @return the remote user
+     */
+    public MessageBytes remoteUser() {
+        return remoteUser;
+    }
+
+    /**
+     * Get the auth type
+     * @return the auth type
+     */
+    public MessageBytes authType() {
+        return authType;
+    }
+
+    /**
+     * Get the query string
+     * @return the query string
+     */
+    public MessageBytes queryString() {
+        return queryString;
+    }
+
+    /**
+     * Get the authorization credentials
+     * @return the authorization credentials
+     */
+    public MessageBytes authorization() {
+        return authorization;
+    }
+
+    /**
+     * Get the scheme
+     * @return the scheme
+     */
+    public String getScheme() {
+        return scheme;
+    }
+
+    /**
+     * Set the scheme.
+     * @param s the scheme
+     */
+    public void setScheme(String s) {
+        scheme = s;
+    }
+
+    /**
+     * Get whether the request is secure or not.
+     * @return <code>true</code> if the request is secure.
+     */
+    public boolean getSecure() {
+        return secure;
+    }
+
+    /**
+     * Set whether the request is secure or not.
+     * @param b <code>true</code> if the request is secure.
+     */
+    public void setSecure(boolean b) {
+        secure = b;
+    }
+
+    /**
+     * Get the content length
+     * @return the content length
+     */
+    public int getContentLength() {
+        return contentLength;
+    }
+
+    /**
+     * Set the content length
+     * @param i the content length
+     */
+    public void setContentLength(int i) {
+        contentLength = i;
+    }
+
+    /**
+     * Get the content type
+     * @return the content type
+     */
+    public MessageBytes contentType() {
+        return contentType;
+    }
+
+    /**
+     * Get this request's headers
+     * @return request headers
+     */
+    public MimeHeaders headers() {
+        return headers;
+    }
+
+    /**
+     * Get cookies.
+     * @return request cookies.
+     */
+    public Cookies cookies() {
+        return cookies;
+    }
+
+    /**
+     * Set an attribute on the request
+     * @param name attribute name
+     * @param value attribute value
+     */
+    public void setAttribute(String name, Object value) {
+        if (name == null || value == null) {
+            return;
+        }
+        attributes.put(name, value);
+    }
+
+    /**
+     * Get an attribute on the request
+     * @param name attribute name
+     * @return attribute value
+     */
+    public Object getAttribute(String name) {
+        if (name == null) {
+            return null;
+        }
+
+        return attributes.get(name);
+    }
+
+    /**
+     * Get iterator over attribute names
+     * @return iterator over attribute names
+     */
+    public Iterator getAttributeNames() {
+        return attributes.keySet().iterator();
+    }
+
+    /**
+     * Get the host id ( or jvmRoute )
+     * @return the jvm route
+     */
+    public MessageBytes instanceId() {
+        return tomcatInstanceId;
+    }
+
+    // backward compat - jvmRoute is the id of this tomcat instance,
+    // used by a load balancer on the server side to implement sticky
+    // sessions, and on the tomcat side to format the session ids.
+    public MessageBytes jvmRoute() {
+        return tomcatInstanceId;
+    }
+
+    private Object notes[]=new Object[16];
+    
+    public final Object getNote(int id) {
+        return notes[id];
+    }
+
+    public final void setNote(int id, Object cr) {
+        notes[id]=cr;
+    }
+    
+    /**
+     * ** SLOW ** for debugging only!
+     */
+    public String toString() {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+
+        pw.println("=== BaseRequest ===");
+        pw.println("method          = " + method.toString());
+        pw.println("protocol        = " + protocol.toString());
+        pw.println("requestURI      = " + requestURI.toString());
+        pw.println("remoteAddr      = " + remoteAddr.toString());
+        pw.println("remoteHost      = " + remoteHost.toString());
+        pw.println("serverName      = " + serverName.toString());
+        pw.println("serverPort      = " + serverPort);
+        pw.println("remoteUser      = " + remoteUser.toString());
+        pw.println("authType        = " + authType.toString());
+        pw.println("queryString     = " + queryString.toString());
+        pw.println("scheme          = " + scheme.toString());
+        pw.println("secure          = " + secure);
+        pw.println("contentLength   = " + contentLength);
+        pw.println("contentType     = " + contentType);
+        pw.println("attributes      = " + attributes.toString());
+        pw.println("headers         = " + headers.toString());
+        pw.println("cookies         = " + cookies.toString());
+        pw.println("jvmRoute        = " + tomcatInstanceId.toString());
+        return sw.toString();
+    }
+    
+}

Added: tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/http/ContentType.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/http/ContentType.java?rev=640706&view=auto
==============================================================================
--- tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/http/ContentType.java (added)
+++ tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/http/ContentType.java Mon Mar 24 23:01:43 2008
@@ -0,0 +1,96 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.http;
+
+
+/**
+ * Usefull methods for Content-Type processing
+ * 
+ * @author James Duncan Davidson [duncan@eng.sun.com]
+ * @author James Todd [gonzo@eng.sun.com]
+ * @author Jason Hunter [jch@eng.sun.com]
+ * @author Harish Prabandham
+ * @author costin@eng.sun.com
+ */
+public class ContentType {
+
+    /**
+     * Parse the character encoding from the specified content type header.
+     * If the content type is null, or there is no explicit character encoding,
+     * <code>null</code> is returned.
+     *
+     * @param contentType a content type header
+     */
+    public static String getCharsetFromContentType(String contentType) {
+
+        if (contentType == null)
+            return (null);
+        int start = contentType.indexOf("charset=");
+        if (start < 0)
+            return (null);
+        String encoding = contentType.substring(start + 8);
+        int end = encoding.indexOf(';');
+        if (end >= 0)
+            encoding = encoding.substring(0, end);
+        encoding = encoding.trim();
+        if ((encoding.length() > 2) && (encoding.startsWith("\""))
+            && (encoding.endsWith("\"")))
+            encoding = encoding.substring(1, encoding.length() - 1);
+        return (encoding.trim());
+
+    }
+
+
+    /**
+     * Returns true if the given content type contains a charset component,
+     * false otherwise.
+     *
+     * @param type Content type
+     * @return true if the given content type contains a charset component,
+     * false otherwise
+     */
+    public static boolean hasCharset(String type) {
+
+        boolean hasCharset = false;
+
+        int len = type.length();
+        int index = type.indexOf(';');
+        while (index != -1) {
+            index++;
+            while (index < len && Character.isSpace(type.charAt(index))) {
+                index++;
+            }
+            if (index+8 < len
+                    && type.charAt(index) == 'c'
+                    && type.charAt(index+1) == 'h'
+                    && type.charAt(index+2) == 'a'
+                    && type.charAt(index+3) == 'r'
+                    && type.charAt(index+4) == 's'
+                    && type.charAt(index+5) == 'e'
+                    && type.charAt(index+6) == 't'
+                    && type.charAt(index+7) == '=') {
+                hasCharset = true;
+                break;
+            }
+            index = type.indexOf(';', index);
+        }
+
+        return hasCharset;
+    }
+
+}

Added: tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/http/Cookies.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/http/Cookies.java?rev=640706&view=auto
==============================================================================
--- tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/http/Cookies.java (added)
+++ tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/http/Cookies.java Mon Mar 24 23:01:43 2008
@@ -0,0 +1,587 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.http;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.StringTokenizer;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+
+/**
+ * A collection of cookies - reusable and tuned for server side performance.
+ * Based on RFC2965 ( and 2109 )
+ *
+ * This class is not synchronized.
+ *
+ * @author Costin Manolache
+ * @author kevin seguin
+ */
+public final class Cookies { // extends MultiMap {
+
+    private static org.apache.juli.logging.Log log=
+        org.apache.juli.logging.LogFactory.getLog(Cookies.class );
+    
+    // expected average number of cookies per request
+    public static final int INITIAL_SIZE=4; 
+    ServerCookie scookies[]=new ServerCookie[INITIAL_SIZE];
+    int cookieCount=0;
+    boolean unprocessed=true;
+
+    MimeHeaders headers;
+
+    /*
+    List of Separator Characters (see isSeparator())
+    Excluding the '/' char violates the RFC, but 
+    it looks like a lot of people put '/'
+    in unquoted values: '/': ; //47 
+    '\t':9 ' ':32 '\"':34 '\'':39 '(':40 ')':41 ',':44 ':':58 ';':59 '<':60 
+    '=':61 '>':62 '?':63 '@':64 '[':91 '\\':92 ']':93 '{':123 '}':125
+    */
+    public static final char SEPARATORS[] = { '\t', ' ', '\"', '\'', '(', ')', ',', 
+        ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '{', '}' };
+
+    protected static final boolean separators[] = new boolean[128];
+    static {
+        for (int i = 0; i < 128; i++) {
+            separators[i] = false;
+        }
+        for (int i = 0; i < SEPARATORS.length; i++) {
+            separators[SEPARATORS[i]] = true;
+        }
+    }
+
+    /**
+     *  Construct a new cookie collection, that will extract
+     *  the information from headers.
+     *
+     * @param headers Cookies are lazy-evaluated and will extract the
+     *     information from the provided headers.
+     */
+    public Cookies(MimeHeaders headers) {
+        this.headers=headers;
+    }
+
+    /**
+     * Construct a new uninitialized cookie collection.
+     * Use {@link #setHeaders} to initialize.
+     */
+    // [seguin] added so that an empty Cookies object could be
+    // created, have headers set, then recycled.
+    public Cookies() {
+    }
+
+    /**
+     * Set the headers from which cookies will be pulled.
+     * This has the side effect of recycling the object.
+     *
+     * @param headers Cookies are lazy-evaluated and will extract the
+     *     information from the provided headers.
+     */
+    // [seguin] added so that an empty Cookies object could be
+    // created, have headers set, then recycled.
+    public void setHeaders(MimeHeaders headers) {
+        recycle();
+        this.headers=headers;
+    }
+
+    /**
+     * Recycle.
+     */
+    public void recycle() {
+            for( int i=0; i< cookieCount; i++ ) {
+            if( scookies[i]!=null )
+                scookies[i].recycle();
+        }
+        cookieCount=0;
+        unprocessed=true;
+    }
+
+    /**
+     * EXPENSIVE!!!  only for debugging.
+     */
+    public String toString() {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        pw.println("=== Cookies ===");
+        int count = getCookieCount();
+        for (int i = 0; i < count; ++i) {
+            pw.println(getCookie(i).toString());
+        }
+        return sw.toString();
+    }
+
+    // -------------------- Indexed access --------------------
+    
+    public ServerCookie getCookie( int idx ) {
+        if( unprocessed ) {
+            getCookieCount(); // will also update the cookies
+        }
+        return scookies[idx];
+    }
+
+    public int getCookieCount() {
+        if( unprocessed ) {
+            unprocessed=false;
+            processCookies(headers);
+        }
+        return cookieCount;
+    }
+
+    // -------------------- Adding cookies --------------------
+
+    /** Register a new, unitialized cookie. Cookies are recycled, and
+     *  most of the time an existing ServerCookie object is returned.
+     *  The caller can set the name/value and attributes for the cookie
+     */
+    public ServerCookie addCookie() {
+        if( cookieCount >= scookies.length  ) {
+            ServerCookie scookiesTmp[]=new ServerCookie[2*cookieCount];
+            System.arraycopy( scookies, 0, scookiesTmp, 0, cookieCount);
+            scookies=scookiesTmp;
+        }
+        
+        ServerCookie c = scookies[cookieCount];
+        if( c==null ) {
+            c= new ServerCookie();
+            scookies[cookieCount]=c;
+        }
+        cookieCount++;
+        return c;
+    }
+
+
+    // code from CookieTools 
+
+    /** Add all Cookie found in the headers of a request.
+     */
+    public  void processCookies( MimeHeaders headers ) {
+        if( headers==null )
+            return;// nothing to process
+        // process each "cookie" header
+        int pos=0;
+        while( pos>=0 ) {
+            // Cookie2: version ? not needed
+            pos=headers.findHeader( "Cookie", pos );
+            // no more cookie headers headers
+            if( pos<0 ) break;
+
+            MessageBytes cookieValue=headers.getValue( pos );
+            if( cookieValue==null || cookieValue.isNull() ) {
+                pos++;
+                continue;
+            }
+
+            // Uncomment to test the new parsing code
+            if( cookieValue.getType() == MessageBytes.T_BYTES ) {
+                if( dbg>0 ) log( "Parsing b[]: " + cookieValue.toString());
+                ByteChunk bc=cookieValue.getByteChunk();
+                processCookieHeader( bc.getBytes(),
+                                     bc.getOffset(),
+                                     bc.getLength());
+            } else {
+                if( dbg>0 ) log( "Parsing S: " + cookieValue.toString());
+                processCookieHeader( cookieValue.toString() );
+            }
+            pos++;// search from the next position
+        }
+    }
+
+    // XXX will be refactored soon!
+    public static boolean equals( String s, byte b[], int start, int end) {
+        int blen = end-start;
+        if (b == null || blen != s.length()) {
+            return false;
+        }
+        int boff = start;
+        for (int i = 0; i < blen; i++) {
+            if (b[boff++] != s.charAt(i)) {
+                return false;
+            }
+        }
+        return true;
+    }
+    
+
+    // ---------------------------------------------------------
+    // -------------------- DEPRECATED, OLD --------------------
+    
+    private void processCookieHeader(  String cookieString )
+    {
+        if( dbg>0 ) log( "Parsing cookie header " + cookieString );
+        // normal cookie, with a string value.
+        // This is the original code, un-optimized - it shouldn't
+        // happen in normal case
+
+        StringTokenizer tok = new StringTokenizer(cookieString,
+                                                  ";", false);
+        while (tok.hasMoreTokens()) {
+            String token = tok.nextToken();
+            int i = token.indexOf("=");
+            if (i > -1) {
+                
+                // XXX
+                // the trims here are a *hack* -- this should
+                // be more properly fixed to be spec compliant
+                
+                String name = token.substring(0, i).trim();
+                String value = token.substring(i+1, token.length()).trim();
+                // RFC 2109 and bug 
+                value=stripQuote( value );
+                ServerCookie cookie = addCookie();
+                
+                cookie.getName().setString(name);
+                cookie.getValue().setString(value);
+                if( dbg > 0 ) log( "Add cookie " + name + "=" + value);
+            } else {
+                // we have a bad cookie.... just let it go
+            }
+        }
+    }
+
+    /**
+     *
+     * Strips quotes from the start and end of the cookie string
+     * This conforms to RFC 2965
+     * 
+     * @param value            a <code>String</code> specifying the cookie 
+     *                         value (possibly quoted).
+     *
+     * @see #setValue
+     *
+     */
+    private static String stripQuote( String value )
+    {
+        //        log("Strip quote from " + value );
+        if (value.startsWith("\"") && value.endsWith("\"")) {
+            try {
+                return value.substring(1,value.length()-1);
+            } catch (Exception ex) { 
+            }
+        }
+        return value;
+    }  
+
+
+    // log
+    static final int dbg=0;
+    public void log(String s ) {
+        if (log.isDebugEnabled())
+            log.debug("Cookies: " + s);
+    }
+
+
+   /**
+     * Returns true if the byte is a separator character as
+     * defined in RFC2619. Since this is called often, this
+     * function should be organized with the most probable
+     * outcomes first.
+     * JVK
+     */
+    public static final boolean isSeparator(final byte c) {
+         if (c > 0 && c < 126)
+             return separators[c];
+         else
+             return false;
+    }
+    
+    /**
+     * Returns true if the byte is a whitespace character as
+     * defined in RFC2619
+     * JVK
+     */
+    public static final boolean isWhiteSpace(final byte c) {
+        // This switch statement is slightly slower
+        // for my vm than the if statement.
+        // Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_07-164)
+        /* 
+        switch (c) {
+        case ' ':;
+        case '\t':;
+        case '\n':;
+        case '\r':;
+        case '\f':;
+            return true;
+        default:;
+            return false;
+        }
+        */
+       if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f')
+           return true;
+       else
+           return false;
+    }
+
+    /**
+     * Parses a cookie header after the initial "Cookie:"
+     * [WS][$]token[WS]=[WS](token|QV)[;|,]
+     * RFC 2965
+     * JVK
+     */
+    public final void processCookieHeader(byte bytes[], int off, int len){
+        if( len<=0 || bytes==null ) return;
+        int end=off+len;
+        int pos=off;
+        int nameStart=0;
+        int nameEnd=0;
+        int valueStart=0;
+        int valueEnd=0;
+        int version = 0;
+        ServerCookie sc=null;
+        boolean isSpecial;
+        boolean isQuoted;
+
+        while (pos < end) {
+            isSpecial = false;
+            isQuoted = false;
+
+            // Skip whitespace and non-token characters (separators)
+            while (pos < end && 
+                   (isSeparator(bytes[pos]) || isWhiteSpace(bytes[pos]))) 
+                {pos++; } 
+
+            if (pos >= end)
+                return;
+
+            // Detect Special cookies
+            if (bytes[pos] == '$') {
+                isSpecial = true;
+                pos++;
+            }
+
+            // Get the cookie name. This must be a token            
+            valueEnd = valueStart = nameStart = pos; 
+            pos = nameEnd = getTokenEndPosition(bytes,pos,end);
+
+            // Skip whitespace
+            while (pos < end && isWhiteSpace(bytes[pos])) {pos++; }; 
+         
+
+            // Check for an '=' -- This could also be a name-only
+            // cookie at the end of the cookie header, so if we
+            // are past the end of the header, but we have a name
+            // skip to the name-only part.
+            if (pos < end && bytes[pos] == '=') {                
+
+                // Skip whitespace
+                do {
+                    pos++;
+                } while (pos < end && isWhiteSpace(bytes[pos])); 
+
+                if (pos >= end)
+                    return;
+
+                // Determine what type of value this is, quoted value,
+                // token, name-only with an '=', or other (bad)
+                switch (bytes[pos]) {
+                case '"':; // Quoted Value
+                    isQuoted = true;
+                    valueStart=pos + 1; // strip "
+                    // getQuotedValue returns the position before 
+                    // at the last qoute. This must be dealt with
+                    // when the bytes are copied into the cookie
+                    valueEnd=getQuotedValueEndPosition(bytes, 
+                                                       valueStart, end);
+                    // We need pos to advance
+                    pos = valueEnd; 
+                    // Handles cases where the quoted value is 
+                    // unterminated and at the end of the header, 
+                    // e.g. [myname="value]
+                    if (pos >= end)
+                        return;
+                    break;
+                case ';':
+                case ',':
+                    // Name-only cookie with an '=' after the name token
+                    // This may not be RFC compliant
+                    valueStart = valueEnd = -1;
+                    // The position is OK (On a delimiter)
+                    break;
+                default:;
+                    if (!isSeparator(bytes[pos])) {
+                        // Token
+                        valueStart=pos;
+                        // getToken returns the position at the delimeter
+                        // or other non-token character
+                        valueEnd=getTokenEndPosition(bytes, valueStart, end);
+                        // We need pos to advance
+                        pos = valueEnd;
+                    } else  {
+                        // INVALID COOKIE, advance to next delimiter
+                        // The starting character of the cookie value was
+                        // not valid.
+                        log("Invalid cookie. Value not a token or quoted value");
+                        while (pos < end && bytes[pos] != ';' && 
+                               bytes[pos] != ',') 
+                            {pos++; };
+                        pos++;
+                        // Make sure no special avpairs can be attributed to 
+                        // the previous cookie by setting the current cookie
+                        // to null
+                        sc = null;
+                        continue;                        
+                    }
+                }
+            } else {
+                // Name only cookie
+                valueStart = valueEnd = -1;
+                pos = nameEnd;
+
+            }
+          
+            // We should have an avpair or name-only cookie at this
+            // point. Perform some basic checks to make sure we are
+            // in a good state.
+  
+            // Skip whitespace
+            while (pos < end && isWhiteSpace(bytes[pos])) {pos++; }; 
+
+
+            // Make sure that after the cookie we have a separator. This
+            // is only important if this is not the last cookie pair
+            while (pos < end && bytes[pos] != ';' && bytes[pos] != ',') { 
+                pos++;
+            }
+            
+            pos++;
+
+            /*
+            if (nameEnd <= nameStart || valueEnd < valueStart ) {
+                // Something is wrong, but this may be a case
+                // of having two ';' characters in a row.
+                // log("Cookie name/value does not conform to RFC 2965");
+                // Advance to next delimiter (ignoring everything else)
+                while (pos < end && bytes[pos] != ';' && bytes[pos] != ',') 
+                    { pos++; };
+                pos++;
+                // Make sure no special cookies can be attributed to 
+                // the previous cookie by setting the current cookie
+                // to null
+                sc = null;
+                continue;
+            }
+            */
+
+            // All checks passed. Add the cookie, start with the 
+            // special avpairs first
+            if (isSpecial) {
+                isSpecial = false;
+                // $Version must be the first avpair in the cookie header
+                // (sc must be null)
+                if (equals( "Version", bytes, nameStart, nameEnd) && 
+                    sc == null) {
+                    // Set version
+                    if( bytes[valueStart] =='1' && valueEnd == (valueStart+1)) {
+                        version=1;
+                    } else {
+                        // unknown version (Versioning is not very strict)
+                    }
+                    continue;
+                } 
+                
+                // We need an active cookie for Path/Port/etc.
+                if (sc == null) {
+                    continue;
+                }
+
+                // Domain is more common, so it goes first
+                if (equals( "Domain", bytes, nameStart, nameEnd)) {
+                    sc.getDomain().setBytes( bytes,
+                                           valueStart,
+                                           valueEnd-valueStart);
+                    continue;
+                } 
+
+                if (equals( "Path", bytes, nameStart, nameEnd)) {
+                    sc.getPath().setBytes( bytes,
+                                           valueStart,
+                                           valueEnd-valueStart);
+                    continue;
+                } 
+
+
+                if (equals( "Port", bytes, nameStart, nameEnd)) {
+                    // sc.getPort is not currently implemented.
+                    // sc.getPort().setBytes( bytes,
+                    //                        valueStart,
+                    //                        valueEnd-valueStart );
+                    continue;
+                } 
+
+                // Unknown cookie, complain
+                log("Unknown Special Cookie");
+
+            } else { // Normal Cookie
+                sc = addCookie();
+                sc.setVersion( version );
+                sc.getName().setBytes( bytes, nameStart,
+                                       nameEnd-nameStart);
+                
+                if (valueStart != -1) { // Normal AVPair
+                    sc.getValue().setBytes( bytes, valueStart,
+                            valueEnd-valueStart);
+                    if (isQuoted) {
+                        // We know this is a byte value so this is safe
+                        ServerCookie.unescapeDoubleQuotes(
+                                sc.getValue().getByteChunk());
+                    }
+                } else {
+                    // Name Only
+                    sc.getValue().setString(""); 
+                }
+                continue;
+            }
+        }
+    }
+
+    /**
+     * Given the starting position of a token, this gets the end of the
+     * token, with no separator characters in between.
+     * JVK
+     */
+    public static final int getTokenEndPosition(byte bytes[], int off, int end){
+        int pos = off;
+        while (pos < end && !isSeparator(bytes[pos])) {pos++; };
+        
+        if (pos > end)
+            return end;
+        return pos;
+    }
+
+    /** 
+     * Given a starting position after an initial quote chracter, this gets
+     * the position of the end quote. This escapes anything after a '\' char
+     * JVK RFC 2616
+     */
+    public static final int getQuotedValueEndPosition(byte bytes[], int off, int end){
+        int pos = off;
+        while (pos < end) {
+            if (bytes[pos] == '"') {
+                return pos;                
+            } else if (bytes[pos] == '\\' && pos < (end - 1)) {
+                pos+=2;
+            } else {
+                pos++;
+            }
+        }
+        // Error, we have reached the end of the header w/o a end quote
+        return end;
+    }
+
+}

Added: tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/http/FastHttpDateFormat.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/http/FastHttpDateFormat.java?rev=640706&view=auto
==============================================================================
--- tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/http/FastHttpDateFormat.java (added)
+++ tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/http/FastHttpDateFormat.java Mon Mar 24 23:01:43 2008
@@ -0,0 +1,231 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.http;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Utility class to generate HTTP dates.
+ * 
+ * @author Remy Maucherat
+ */
+public final class FastHttpDateFormat {
+
+
+    // -------------------------------------------------------------- Variables
+
+
+    protected static final int CACHE_SIZE = 
+        Integer.parseInt(System.getProperty("org.apache.tomcat.util.http.FastHttpDateFormat.CACHE_SIZE", "1000"));
+
+    
+    /**
+     * HTTP date format.
+     */
+    protected static final SimpleDateFormat format = 
+        new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
+
+
+    /**
+     * The set of SimpleDateFormat formats to use in getDateHeader().
+     */
+    protected static final SimpleDateFormat formats[] = {
+        new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
+        new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
+        new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
+    };
+
+
+    protected final static TimeZone gmtZone = TimeZone.getTimeZone("GMT");
+
+
+    /**
+     * GMT timezone - all HTTP dates are on GMT
+     */
+    static {
+
+        format.setTimeZone(gmtZone);
+
+        formats[0].setTimeZone(gmtZone);
+        formats[1].setTimeZone(gmtZone);
+        formats[2].setTimeZone(gmtZone);
+
+    }
+
+
+    /**
+     * Instant on which the currentDate object was generated.
+     */
+    protected static long currentDateGenerated = 0L;
+
+
+    /**
+     * Current formatted date.
+     */
+    protected static String currentDate = null;
+
+
+    /**
+     * Formatter cache.
+     */
+    protected static final ConcurrentHashMap<Long, String> formatCache = 
+        new ConcurrentHashMap<Long, String>(CACHE_SIZE);
+
+
+    /**
+     * Parser cache.
+     */
+    protected static final ConcurrentHashMap<String, Long> parseCache = 
+        new ConcurrentHashMap<String, Long>(CACHE_SIZE);
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Get the current date in HTTP format.
+     */
+    public static final String getCurrentDate() {
+
+        long now = System.currentTimeMillis();
+        if ((now - currentDateGenerated) > 1000) {
+            synchronized (format) {
+                if ((now - currentDateGenerated) > 1000) {
+                    currentDateGenerated = now;
+                    currentDate = format.format(new Date(now));
+                }
+            }
+        }
+        return currentDate;
+
+    }
+
+
+    /**
+     * Get the HTTP format of the specified date.
+     */
+    public static final String formatDate
+        (long value, DateFormat threadLocalformat) {
+
+        Long longValue = new Long(value);
+        String cachedDate = formatCache.get(longValue);
+        if (cachedDate != null)
+            return cachedDate;
+
+        String newDate = null;
+        Date dateValue = new Date(value);
+        if (threadLocalformat != null) {
+            newDate = threadLocalformat.format(dateValue);
+            updateFormatCache(longValue, newDate);
+        } else {
+            synchronized (formatCache) {
+                synchronized (format) {
+                    newDate = format.format(dateValue);
+                }
+                updateFormatCache(longValue, newDate);
+            }
+        }
+        return newDate;
+
+    }
+
+
+    /**
+     * Try to parse the given date as a HTTP date.
+     */
+    public static final long parseDate(String value, 
+                                       DateFormat[] threadLocalformats) {
+
+        Long cachedDate = parseCache.get(value);
+        if (cachedDate != null)
+            return cachedDate.longValue();
+
+        Long date = null;
+        if (threadLocalformats != null) {
+            date = internalParseDate(value, threadLocalformats);
+            updateParseCache(value, date);
+        } else {
+            synchronized (parseCache) {
+                date = internalParseDate(value, formats);
+                updateParseCache(value, date);
+            }
+        }
+        if (date == null) {
+            return (-1L);
+        } else {
+            return date.longValue();
+        }
+
+    }
+
+
+    /**
+     * Parse date with given formatters.
+     */
+    private static final Long internalParseDate
+        (String value, DateFormat[] formats) {
+        Date date = null;
+        for (int i = 0; (date == null) && (i < formats.length); i++) {
+            try {
+                date = formats[i].parse(value);
+            } catch (ParseException e) {
+                ;
+            }
+        }
+        if (date == null) {
+            return null;
+        }
+        return new Long(date.getTime());
+    }
+
+
+    /**
+     * Update cache.
+     */
+    private static void updateFormatCache(Long key, String value) {
+        if (value == null) {
+            return;
+        }
+        if (formatCache.size() > CACHE_SIZE) {
+            formatCache.clear();
+        }
+        formatCache.put(key, value);
+    }
+
+
+    /**
+     * Update cache.
+     */
+    private static void updateParseCache(String key, Long value) {
+        if (value == null) {
+            return;
+        }
+        if (parseCache.size() > CACHE_SIZE) {
+            parseCache.clear();
+        }
+        parseCache.put(key, value);
+    }
+
+
+}

Added: tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/http/HttpMessages.java
URL: http://svn.apache.org/viewvc/tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/http/HttpMessages.java?rev=640706&view=auto
==============================================================================
--- tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/http/HttpMessages.java (added)
+++ tomcat/sandbox/tomcat-lite/tomcat-coyote/org/apache/tomcat/util/http/HttpMessages.java Mon Mar 24 23:01:43 2008
@@ -0,0 +1,107 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.http;
+
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * Handle (internationalized) HTTP messages.
+ * 
+ * @author James Duncan Davidson [duncan@eng.sun.com]
+ * @author James Todd [gonzo@eng.sun.com]
+ * @author Jason Hunter [jch@eng.sun.com]
+ * @author Harish Prabandham
+ * @author costin@eng.sun.com
+ */
+public class HttpMessages {
+    // XXX move message resources in this package
+    protected static StringManager sm =
+        StringManager.getManager("org.apache.tomcat.util.http.res");
+	
+    static String st_200=null;
+    static String st_302=null;
+    static String st_400=null;
+    static String st_404=null;
+    
+    /** Get the status string associated with a status code.
+     *  No I18N - return the messages defined in the HTTP spec.
+     *  ( the user isn't supposed to see them, this is the last
+     *  thing to translate)
+     *
+     *  Common messages are cached.
+     *
+     */
+    public static String getMessage( int status ) {
+	// method from Response.
+	
+	// Does HTTP requires/allow international messages or
+	// are pre-defined? The user doesn't see them most of the time
+	switch( status ) {
+	case 200:
+	    if( st_200==null ) st_200=sm.getString( "sc.200");
+	    return st_200;
+	case 302:
+	    if( st_302==null ) st_302=sm.getString( "sc.302");
+	    return st_302;
+	case 400:
+	    if( st_400==null ) st_400=sm.getString( "sc.400");
+	    return st_400;
+	case 404:
+	    if( st_404==null ) st_404=sm.getString( "sc.404");
+	    return st_404;
+	}
+	return sm.getString("sc."+ status);
+    }
+
+    /**
+     * Filter the specified message string for characters that are sensitive
+     * in HTML.  This avoids potential attacks caused by including JavaScript
+     * codes in the request URL that is often reported in error messages.
+     *
+     * @param message The message string to be filtered
+     */
+    public static String filter(String message) {
+
+	if (message == null)
+	    return (null);
+
+	char content[] = new char[message.length()];
+	message.getChars(0, message.length(), content, 0);
+	StringBuffer result = new StringBuffer(content.length + 50);
+	for (int i = 0; i < content.length; i++) {
+	    switch (content[i]) {
+	    case '<':
+		result.append("&lt;");
+		break;
+	    case '>':
+		result.append("&gt;");
+		break;
+	    case '&':
+		result.append("&amp;");
+		break;
+	    case '"':
+		result.append("&quot;");
+		break;
+	    default:
+		result.append(content[i]);
+	    }
+	}
+	return (result.toString());
+    }
+
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Mime
View raw message