升级Tomcat版本 apache-tomcat-7.0.77
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/CookieExample.class b/tomcat-uid/webapps/examples/WEB-INF/classes/CookieExample.class
index bc8e321..5b481e7 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/CookieExample.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/CookieExample.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/CookieExample.java b/tomcat-uid/webapps/examples/WEB-INF/classes/CookieExample.java
index 5d7d5ce..b3e1c09 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/CookieExample.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/CookieExample.java
@@ -15,10 +15,15 @@
 * limitations under the License.

 */

 

-import java.io.*;

-import java.util.*;

-import javax.servlet.*;

-import javax.servlet.http.*;

+import java.io.IOException;

+import java.io.PrintWriter;

+import java.util.ResourceBundle;

+

+import javax.servlet.ServletException;

+import javax.servlet.http.Cookie;

+import javax.servlet.http.HttpServlet;

+import javax.servlet.http.HttpServletRequest;

+import javax.servlet.http.HttpServletResponse;

 

 import util.HTMLFilter;

 

@@ -30,29 +35,42 @@
 

 public class CookieExample extends HttpServlet {

 

-    ResourceBundle rb = ResourceBundle.getBundle("LocalStrings");

-    

+    private static final long serialVersionUID = 1L;

+

+    private static final ResourceBundle RB = ResourceBundle.getBundle("LocalStrings");

+

+    @Override

     public void doGet(HttpServletRequest request,

                       HttpServletResponse response)

         throws IOException, ServletException

     {

+

+        String cookieName = request.getParameter("cookiename");

+        String cookieValue = request.getParameter("cookievalue");

+        Cookie aCookie = null;

+        if (cookieName != null && cookieValue != null) {

+            aCookie = new Cookie(cookieName, cookieValue);

+            aCookie.setPath(request.getContextPath() + "/");

+            response.addCookie(aCookie);

+        }

+

         response.setContentType("text/html");

 

         PrintWriter out = response.getWriter();

         out.println("<html>");

         out.println("<head>");

 

-        String title = rb.getString("cookies.title");

+        String title = RB.getString("cookies.title");

         out.println("<title>" + title + "</title>");

         out.println("</head>");

         out.println("<body bgcolor=\"white\">");

 

-	// relative links

+        // relative links

 

         // XXX

         // making these absolute till we work out the

-        // addition of a PathInfo issue 

-	

+        // addition of a PathInfo issue

+

         out.println("<a href=\"../cookies.html\">");

         out.println("<img src=\"../images/code.gif\" height=24 " +

                     "width=24 align=right border=0 alt=\"view code\"></a>");

@@ -64,47 +82,44 @@
 

         Cookie[] cookies = request.getCookies();

         if ((cookies != null) && (cookies.length > 0)) {

-            out.println(rb.getString("cookies.cookies") + "<br>");

+            out.println(RB.getString("cookies.cookies") + "<br>");

             for (int i = 0; i < cookies.length; i++) {

                 Cookie cookie = cookies[i];

                 out.print("Cookie Name: " + HTMLFilter.filter(cookie.getName())

                           + "<br>");

-                out.println("  Cookie Value: " 

+                out.println("  Cookie Value: "

                             + HTMLFilter.filter(cookie.getValue())

                             + "<br><br>");

             }

         } else {

-            out.println(rb.getString("cookies.no-cookies"));

+            out.println(RB.getString("cookies.no-cookies"));

         }

 

-        String cookieName = request.getParameter("cookiename");

-        String cookieValue = request.getParameter("cookievalue");

-        if (cookieName != null && cookieValue != null) {

-            Cookie cookie = new Cookie(cookieName, cookieValue);

-            response.addCookie(cookie);

+        if (aCookie != null) {

             out.println("<P>");

-            out.println(rb.getString("cookies.set") + "<br>");

-            out.print(rb.getString("cookies.name") + "  " 

+            out.println(RB.getString("cookies.set") + "<br>");

+            out.print(RB.getString("cookies.name") + "  "

                       + HTMLFilter.filter(cookieName) + "<br>");

-            out.print(rb.getString("cookies.value") + "  " 

+            out.print(RB.getString("cookies.value") + "  "

                       + HTMLFilter.filter(cookieValue));

         }

-        

+

         out.println("<P>");

-        out.println(rb.getString("cookies.make-cookie") + "<br>");

+        out.println(RB.getString("cookies.make-cookie") + "<br>");

         out.print("<form action=\"");

         out.println("CookieExample\" method=POST>");

-        out.print(rb.getString("cookies.name") + "  ");

+        out.print(RB.getString("cookies.name") + "  ");

         out.println("<input type=text length=20 name=cookiename><br>");

-        out.print(rb.getString("cookies.value") + "  ");

+        out.print(RB.getString("cookies.value") + "  ");

         out.println("<input type=text length=20 name=cookievalue><br>");

         out.println("<input type=submit></form>");

-            

-            

+

+

         out.println("</body>");

         out.println("</html>");

     }

 

+    @Override

     public void doPost(HttpServletRequest request,

                       HttpServletResponse response)

         throws IOException, ServletException

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/HelloWorldExample.class b/tomcat-uid/webapps/examples/WEB-INF/classes/HelloWorldExample.class
index 0d3c85e..41e3c40 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/HelloWorldExample.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/HelloWorldExample.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/HelloWorldExample.java b/tomcat-uid/webapps/examples/WEB-INF/classes/HelloWorldExample.java
index 3702542..9902e3b 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/HelloWorldExample.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/HelloWorldExample.java
@@ -14,11 +14,14 @@
 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

+import java.io.IOException;

+import java.io.PrintWriter;

+import java.util.ResourceBundle;

 

-import java.io.*;

-import java.util.*;

-import javax.servlet.*;

-import javax.servlet.http.*;

+import javax.servlet.ServletException;

+import javax.servlet.http.HttpServlet;

+import javax.servlet.http.HttpServletRequest;

+import javax.servlet.http.HttpServletResponse;

 

 /**

  * The simplest possible servlet.

@@ -28,7 +31,9 @@
 

 public class HelloWorldExample extends HttpServlet {

 

+    private static final long serialVersionUID = 1L;

 

+    @Override

     public void doGet(HttpServletRequest request,

                       HttpServletResponse response)

         throws IOException, ServletException

@@ -41,22 +46,22 @@
         out.println("<html>");

         out.println("<head>");

 

-	    String title = rb.getString("helloworld.title");

+        String title = rb.getString("helloworld.title");

 

-	    out.println("<title>" + title + "</title>");

+        out.println("<title>" + title + "</title>");

         out.println("</head>");

         out.println("<body bgcolor=\"white\">");

 

-	// note that all links are created to be relative. this

-	// ensures that we can move the web application that this

-	// servlet belongs to to a different place in the url

-	// tree and not have any harmful side effects.

+        // note that all links are created to be relative. this

+        // ensures that we can move the web application that this

+        // servlet belongs to to a different place in the url

+        // tree and not have any harmful side effects.

 

         // XXX

         // making these absolute till we work out the

         // addition of a PathInfo issue

 

-	    out.println("<a href=\"../helloworld.html\">");

+        out.println("<a href=\"../helloworld.html\">");

         out.println("<img src=\"../images/code.gif\" height=24 " +

                     "width=24 align=right border=0 alt=\"view code\"></a>");

         out.println("<a href=\"../index.html\">");

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/LocalStrings_es.properties b/tomcat-uid/webapps/examples/WEB-INF/classes/LocalStrings_es.properties
index f5a1261..009e34d 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/LocalStrings_es.properties
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/LocalStrings_es.properties
@@ -12,40 +12,31 @@
 # 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.

-

-# Default localized string information

-# Localized para Locale es_ES

-

-helloworld.title=Hola Mundo!

-

-requestinfo.title=Ejemplo de Informacion de Request

-requestinfo.label.method=Metodo:

-requestinfo.label.requesturi=Request URI:

-requestinfo.label.protocol=Protocolo:

-requestinfo.label.pathinfo=Path Info:

-requestinfo.label.remoteaddr=Direccion Remota:

-

-requestheader.title=Ejemplo de Cabecera de Request

-

-requestparams.title=Ejemplo de parametros de Request

-requestparams.params-in-req=Parametros en este Request:

-requestparams.no-params=No hay parametro. por favor usa alguno

-requestparams.firstname=Nombre:

-requestparams.lastname=Apellidos:

-

-cookies.title=Ejemplo de Cookies

-cookies.cookies=Tu navegador esta enviando los siguientes cookies:

-cookies.no-cookies=Tu navegador no esta enviando cookies

-cookies.make-cookie=Crea un cookie para enviarlo a tu navegador

-cookies.name=Nombre:

-cookies.value=Valor:

-cookies.set=Acabas de enviar a tu navegador estos cookies:

-

-sessions.title=ejemplo de Sesiones

-sessions.id=ID de Sesion:

-sessions.created=Creado:

-sessions.lastaccessed=Ultimo Acceso:

-sessions.data=Lo siguientes datos estan en tu sesion:

-sessions.adddata=A\u00f1ade datos a tu sesion:

-sessions.dataname=Nombre del atributo de sesion:

-sessions.datavalue=Valor del atributo de sesion:

+helloworld.title = Hola Mundo\!

+requestinfo.title = Ejemplo de Informacion de Requerimiento\:

+requestinfo.label.method = M\u00E9todo\:

+requestinfo.label.requesturi = URI de Requerimiento\:

+requestinfo.label.protocol = Protocolo\:

+requestinfo.label.pathinfo = Info de Ruta\:

+requestinfo.label.remoteaddr = Direccion Remota\:

+requestheader.title = Ejemplo de Cabecera de Requerimiento\:

+requestparams.title = Ejemplo de par\u00E1metros de Requerimiento\:

+requestparams.params-in-req = Par\u00E1metros en este Request\:

+requestparams.no-params = No hay p\u00E1rametro. Por favor, usa alguno

+requestparams.firstname = Nombre\:

+requestparams.lastname = Apellidos\:

+cookies.title = Ejemplo de Cookies

+cookies.cookies = Tu navegador est\u00E1 enviando los siguientes cookies\:

+cookies.no-cookies = Tu navegador no est\u00E1 enviando cookies

+cookies.make-cookie = Crea un cookie para enviarlo a tu navegador

+cookies.name = Nombre\:

+cookies.value = Valor\:

+cookies.set = Acabas de enviar a tu navegador estos cookies\:

+sessions.title = Ejemplo de Sesiones

+sessions.id = ID de Sesi\u00F3n\:

+sessions.created = Creado\:

+sessions.lastaccessed = Ultimo Acceso\:

+sessions.data = Lo siguientes datos est\u00E1n en tu sesi\u00F3n\:

+sessions.adddata = A\u00F1ade datos a tu sesi\u00F3n\:

+sessions.dataname = Nombre del atributo de sesi\u00F3n\:

+sessions.datavalue = Valor del atributo de sesi\u00F3n\:

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/RequestHeaderExample.class b/tomcat-uid/webapps/examples/WEB-INF/classes/RequestHeaderExample.class
index 8270f63..23151e8 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/RequestHeaderExample.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/RequestHeaderExample.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/RequestHeaderExample.java b/tomcat-uid/webapps/examples/WEB-INF/classes/RequestHeaderExample.java
index 2a550f2..2010767 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/RequestHeaderExample.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/RequestHeaderExample.java
@@ -15,11 +15,19 @@
 * limitations under the License.

 */

 

-import java.io.*;

-import java.util.*;

-import javax.servlet.*;

-import javax.servlet.http.*;

+import java.io.IOException;

+import java.io.PrintWriter;

+import java.util.Enumeration;

+import java.util.Locale;

+import java.util.ResourceBundle;

 

+import javax.servlet.ServletException;

+import javax.servlet.http.HttpServlet;

+import javax.servlet.http.HttpServletRequest;

+import javax.servlet.http.HttpServletResponse;

+import javax.servlet.http.HttpSession;

+

+import util.CookieFilter;

 import util.HTMLFilter;

 

 /**

@@ -30,8 +38,11 @@
 

 public class RequestHeaderExample extends HttpServlet {

 

-    ResourceBundle rb = ResourceBundle.getBundle("LocalStrings");

-    

+    private static final long serialVersionUID = 1L;

+

+    private static final ResourceBundle RB = ResourceBundle.getBundle("LocalStrings");

+

+    @Override

     public void doGet(HttpServletRequest request,

                       HttpServletResponse response)

         throws IOException, ServletException

@@ -42,17 +53,17 @@
         out.println("<html>");

         out.println("<head>");

 

-        String title = rb.getString("requestheader.title");

+        String title = RB.getString("requestheader.title");

         out.println("<title>" + title + "</title>");

         out.println("</head>");

         out.println("<body bgcolor=\"white\">");

 

-	// all links relative

+        // all links relative

 

         // XXX

         // making these absolute till we work out the

-        // addition of a PathInfo issue 

-	

+        // addition of a PathInfo issue

+

         out.println("<a href=\"../reqheaders.html\">");

         out.println("<img src=\"../images/code.gif\" height=24 " +

                     "width=24 align=right border=0 alt=\"view code\"></a>");

@@ -62,19 +73,29 @@
 

         out.println("<h3>" + title + "</h3>");

         out.println("<table border=0>");

-        Enumeration e = request.getHeaderNames();

+        Enumeration<String> e = request.getHeaderNames();

         while (e.hasMoreElements()) {

-            String headerName = (String)e.nextElement();

+            String headerName = e.nextElement();

             String headerValue = request.getHeader(headerName);

             out.println("<tr><td bgcolor=\"#CCCCCC\">");

             out.println(HTMLFilter.filter(headerName));

             out.println("</td><td>");

-            out.println(HTMLFilter.filter(headerValue));

+            if (headerName.toLowerCase(Locale.ENGLISH).contains("cookie")) {

+                HttpSession session = request.getSession(false);

+                String sessionId = null;

+                if (session != null) {

+                    sessionId = session.getId();

+                }

+                out.println(HTMLFilter.filter(CookieFilter.filter(headerValue, sessionId)));

+            } else {

+                out.println(HTMLFilter.filter(headerValue));

+            }

             out.println("</td></tr>");

         }

         out.println("</table>");

     }

 

+    @Override

     public void doPost(HttpServletRequest request,

                       HttpServletResponse response)

         throws IOException, ServletException

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/RequestInfoExample.class b/tomcat-uid/webapps/examples/WEB-INF/classes/RequestInfoExample.class
index e93ed68..b060caf 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/RequestInfoExample.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/RequestInfoExample.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/RequestInfoExample.java b/tomcat-uid/webapps/examples/WEB-INF/classes/RequestInfoExample.java
index 86313e5..1f429e8 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/RequestInfoExample.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/RequestInfoExample.java
@@ -15,10 +15,14 @@
 * limitations under the License.

 */

 

-import java.io.*;

-import java.util.*;

-import javax.servlet.*;

-import javax.servlet.http.*;

+import java.io.IOException;

+import java.io.PrintWriter;

+import java.util.ResourceBundle;

+

+import javax.servlet.ServletException;

+import javax.servlet.http.HttpServlet;

+import javax.servlet.http.HttpServletRequest;

+import javax.servlet.http.HttpServletResponse;

 

 import util.HTMLFilter;

 

@@ -30,9 +34,11 @@
 

 public class RequestInfoExample extends HttpServlet {

 

+    private static final long serialVersionUID = 1L;

 

-    ResourceBundle rb = ResourceBundle.getBundle("LocalStrings");

+    private static final ResourceBundle RB = ResourceBundle.getBundle("LocalStrings");

 

+    @Override

     public void doGet(HttpServletRequest request,

                       HttpServletResponse response)

         throws IOException, ServletException

@@ -43,7 +49,7 @@
         out.println("<html>");

         out.println("<head>");

 

-        String title = rb.getString("requestinfo.title");

+        String title = RB.getString("requestinfo.title");

         out.println("<title>" + title + "</title>");

         out.println("</head>");

         out.println("<body bgcolor=\"white\">");

@@ -64,23 +70,23 @@
 

         out.println("<h3>" + title + "</h3>");

         out.println("<table border=0><tr><td>");

-        out.println(rb.getString("requestinfo.label.method"));

+        out.println(RB.getString("requestinfo.label.method"));

         out.println("</td><td>");

         out.println(HTMLFilter.filter(request.getMethod()));

         out.println("</td></tr><tr><td>");

-        out.println(rb.getString("requestinfo.label.requesturi"));

+        out.println(RB.getString("requestinfo.label.requesturi"));

         out.println("</td><td>");

         out.println(HTMLFilter.filter(request.getRequestURI()));

         out.println("</td></tr><tr><td>");

-        out.println(rb.getString("requestinfo.label.protocol"));

+        out.println(RB.getString("requestinfo.label.protocol"));

         out.println("</td><td>");

         out.println(HTMLFilter.filter(request.getProtocol()));

         out.println("</td></tr><tr><td>");

-        out.println(rb.getString("requestinfo.label.pathinfo"));

+        out.println(RB.getString("requestinfo.label.pathinfo"));

         out.println("</td><td>");

         out.println(HTMLFilter.filter(request.getPathInfo()));

         out.println("</td></tr><tr><td>");

-        out.println(rb.getString("requestinfo.label.remoteaddr"));

+        out.println(RB.getString("requestinfo.label.remoteaddr"));

         out.println("</td><td>");

         out.println(HTMLFilter.filter(request.getRemoteAddr()));

         out.println("</td></tr>");

@@ -98,6 +104,7 @@
         out.println("</table>");

     }

 

+    @Override

     public void doPost(HttpServletRequest request,

                       HttpServletResponse response)

         throws IOException, ServletException

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/RequestParamExample.class b/tomcat-uid/webapps/examples/WEB-INF/classes/RequestParamExample.class
index dccd0ba..a0233b4 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/RequestParamExample.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/RequestParamExample.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/RequestParamExample.java b/tomcat-uid/webapps/examples/WEB-INF/classes/RequestParamExample.java
index 2b8b77c..a4c5c67 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/RequestParamExample.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/RequestParamExample.java
@@ -15,10 +15,14 @@
 * limitations under the License.

 */

 

-import java.io.*;

-import java.util.*;

-import javax.servlet.*;

-import javax.servlet.http.*;

+import java.io.IOException;

+import java.io.PrintWriter;

+import java.util.ResourceBundle;

+

+import javax.servlet.ServletException;

+import javax.servlet.http.HttpServlet;

+import javax.servlet.http.HttpServletRequest;

+import javax.servlet.http.HttpServletResponse;

 

 import util.HTMLFilter;

 

@@ -30,9 +34,11 @@
 

 public class RequestParamExample extends HttpServlet {

 

+    private static final long serialVersionUID = 1L;

 

-    ResourceBundle rb = ResourceBundle.getBundle("LocalStrings");

-    

+    private static final ResourceBundle RB = ResourceBundle.getBundle("LocalStrings");

+

+    @Override

     public void doGet(HttpServletRequest request,

                       HttpServletResponse response)

         throws IOException, ServletException

@@ -43,19 +49,19 @@
         out.println("<html>");

         out.println("<head>");

 

-        String title = rb.getString("requestparams.title");

+        String title = RB.getString("requestparams.title");

         out.println("<title>" + title + "</title>");

         out.println("</head>");

         out.println("<body bgcolor=\"white\">");

 

         // img stuff not req'd for source code html showing

 

-	// all links relative

+       // all links relative

 

         // XXX

         // making these absolute till we work out the

-        // addition of a PathInfo issue 

-	

+        // addition of a PathInfo issue

+

         out.println("<a href=\"../reqparams.html\">");

         out.println("<img src=\"../images/code.gif\" height=24 " +

                     "width=24 align=right border=0 alt=\"view code\"></a>");

@@ -66,23 +72,23 @@
         out.println("<h3>" + title + "</h3>");

         String firstName = request.getParameter("firstname");

         String lastName = request.getParameter("lastname");

-        out.println(rb.getString("requestparams.params-in-req") + "<br>");

+        out.println(RB.getString("requestparams.params-in-req") + "<br>");

         if (firstName != null || lastName != null) {

-            out.println(rb.getString("requestparams.firstname"));

+            out.println(RB.getString("requestparams.firstname"));

             out.println(" = " + HTMLFilter.filter(firstName) + "<br>");

-            out.println(rb.getString("requestparams.lastname"));

+            out.println(RB.getString("requestparams.lastname"));

             out.println(" = " + HTMLFilter.filter(lastName));

         } else {

-            out.println(rb.getString("requestparams.no-params"));

+            out.println(RB.getString("requestparams.no-params"));

         }

         out.println("<P>");

         out.print("<form action=\"");

         out.print("RequestParamExample\" ");

         out.println("method=POST>");

-        out.println(rb.getString("requestparams.firstname"));

+        out.println(RB.getString("requestparams.firstname"));

         out.println("<input type=text size=20 name=firstname>");

         out.println("<br>");

-        out.println(rb.getString("requestparams.lastname"));

+        out.println(RB.getString("requestparams.lastname"));

         out.println("<input type=text size=20 name=lastname>");

         out.println("<br>");

         out.println("<input type=submit>");

@@ -92,6 +98,7 @@
         out.println("</html>");

     }

 

+    @Override

     public void doPost(HttpServletRequest request,

                       HttpServletResponse response)

         throws IOException, ServletException

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/SessionExample.class b/tomcat-uid/webapps/examples/WEB-INF/classes/SessionExample.class
index 372e463..232cc36 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/SessionExample.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/SessionExample.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/SessionExample.java b/tomcat-uid/webapps/examples/WEB-INF/classes/SessionExample.java
index 33f4f8f..cca91e0 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/SessionExample.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/SessionExample.java
@@ -15,10 +15,17 @@
 * limitations under the License.

 */

 

-import java.io.*;

-import java.util.*;

-import javax.servlet.*;

-import javax.servlet.http.*;

+import java.io.IOException;

+import java.io.PrintWriter;

+import java.util.Date;

+import java.util.Enumeration;

+import java.util.ResourceBundle;

+

+import javax.servlet.ServletException;

+import javax.servlet.http.HttpServlet;

+import javax.servlet.http.HttpServletRequest;

+import javax.servlet.http.HttpServletResponse;

+import javax.servlet.http.HttpSession;

 

 import util.HTMLFilter;

 

@@ -30,8 +37,11 @@
 

 public class SessionExample extends HttpServlet {

 

-    ResourceBundle rb = ResourceBundle.getBundle("LocalStrings");

-    

+    private static final long serialVersionUID = 1L;

+

+    private static final ResourceBundle RB = ResourceBundle.getBundle("LocalStrings");

+

+    @Override

     public void doGet(HttpServletRequest request,

                       HttpServletResponse response)

         throws IOException, ServletException

@@ -42,18 +52,18 @@
         out.println("<html>");

         out.println("<head>");

 

-        String title = rb.getString("sessions.title");

+        String title = RB.getString("sessions.title");

         out.println("<title>" + title + "</title>");

         out.println("</head>");

         out.println("<body bgcolor=\"white\">");

 

         // img stuff not req'd for source code html showing

-	// relative links everywhere!

+        // relative links everywhere!

 

         // XXX

         // making these absolute till we work out the

-        // addition of a PathInfo issue 

-	

+        // addition of a PathInfo issue

+

         out.println("<a href=\"../sessions.html\">");

         out.println("<img src=\"../images/code.gif\" height=24 " +

                     "width=24 align=right border=0 alt=\"view code\"></a>");

@@ -64,11 +74,11 @@
         out.println("<h3>" + title + "</h3>");

 

         HttpSession session = request.getSession(true);

-        out.println(rb.getString("sessions.id") + " " + session.getId());

+        out.println(RB.getString("sessions.id") + " " + session.getId());

         out.println("<br>");

-        out.println(rb.getString("sessions.created") + " ");

+        out.println(RB.getString("sessions.created") + " ");

         out.println(new Date(session.getCreationTime()) + "<br>");

-        out.println(rb.getString("sessions.lastaccessed") + " ");

+        out.println(RB.getString("sessions.lastaccessed") + " ");

         out.println(new Date(session.getLastAccessedTime()));

 

         String dataName = request.getParameter("dataname");

@@ -78,24 +88,24 @@
         }

 

         out.println("<P>");

-        out.println(rb.getString("sessions.data") + "<br>");

-        Enumeration names = session.getAttributeNames();

+        out.println(RB.getString("sessions.data") + "<br>");

+        Enumeration<String> names = session.getAttributeNames();

         while (names.hasMoreElements()) {

-            String name = (String) names.nextElement(); 

+            String name = names.nextElement();

             String value = session.getAttribute(name).toString();

-            out.println(HTMLFilter.filter(name) + " = " 

+            out.println(HTMLFilter.filter(name) + " = "

                         + HTMLFilter.filter(value) + "<br>");

         }

 

         out.println("<P>");

         out.print("<form action=\"");

-	out.print(response.encodeURL("SessionExample"));

+        out.print(response.encodeURL("SessionExample"));

         out.print("\" ");

         out.println("method=POST>");

-        out.println(rb.getString("sessions.dataname"));

+        out.println(RB.getString("sessions.dataname"));

         out.println("<input type=text size=20 name=dataname>");

         out.println("<br>");

-        out.println(rb.getString("sessions.datavalue"));

+        out.println(RB.getString("sessions.datavalue"));

         out.println("<input type=text size=20 name=datavalue>");

         out.println("<br>");

         out.println("<input type=submit>");

@@ -103,13 +113,13 @@
 

         out.println("<P>GET based form:<br>");

         out.print("<form action=\"");

-	out.print(response.encodeURL("SessionExample"));

+        out.print(response.encodeURL("SessionExample"));

         out.print("\" ");

         out.println("method=GET>");

-        out.println(rb.getString("sessions.dataname"));

+        out.println(RB.getString("sessions.dataname"));

         out.println("<input type=text size=20 name=dataname>");

         out.println("<br>");

-        out.println(rb.getString("sessions.datavalue"));

+        out.println(RB.getString("sessions.datavalue"));

         out.println("<input type=text size=20 name=datavalue>");

         out.println("<br>");

         out.println("<input type=submit>");

@@ -118,11 +128,12 @@
         out.print("<p><a href=\"");

         out.print(HTMLFilter.filter(response.encodeURL("SessionExample?dataname=foo&datavalue=bar")));

         out.println("\" >URL encoded </a>");

-	

+

         out.println("</body>");

         out.println("</html>");

     }

 

+    @Override

     public void doPost(HttpServletRequest request,

                       HttpServletResponse response)

         throws IOException, ServletException

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async0$1.class b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async0$1.class
new file mode 100644
index 0000000..021733f
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async0$1.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async0.class b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async0.class
new file mode 100644
index 0000000..16fde39
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async0.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async0.java b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async0.java
new file mode 100644
index 0000000..ea4df76
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async0.java
@@ -0,0 +1,67 @@
+/*

+* 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 async;

+

+import java.io.IOException;

+

+import javax.servlet.AsyncContext;

+import javax.servlet.ServletException;

+import javax.servlet.http.HttpServlet;

+import javax.servlet.http.HttpServletRequest;

+import javax.servlet.http.HttpServletResponse;

+

+import org.apache.juli.logging.Log;

+import org.apache.juli.logging.LogFactory;

+

+public class Async0 extends HttpServlet {

+

+    private static final long serialVersionUID = 1L;

+

+    private static final Log log = LogFactory.getLog(Async0.class);

+

+    @Override

+    protected void service(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {

+        if (Boolean.TRUE.equals(req.getAttribute("dispatch"))) {

+            log.info("Received dispatch, completing on the worker thread.");

+            log.info("After complete called started:"+req.isAsyncStarted());

+            resp.getWriter().write("Async dispatch worked:+"+System.currentTimeMillis()+"\n");

+        } else {

+            resp.setContentType("text/plain");

+            final AsyncContext actx = req.startAsync();

+            actx.setTimeout(Long.MAX_VALUE);

+            Runnable run = new Runnable() {

+                @Override

+                public void run() {

+                    try {

+                        req.setAttribute("dispatch", Boolean.TRUE);

+                        Thread.currentThread().setName("Async0-Thread");

+                        log.info("Putting AsyncThread to sleep");

+                        Thread.sleep(2*1000);

+                        log.info("Dispatching");

+                        actx.dispatch();

+                    }catch (InterruptedException x) {

+                        log.error("Async1",x);

+                    }catch (IllegalStateException x) {

+                        log.error("Async1",x);

+                    }

+                }

+            };

+            Thread t = new Thread(run);

+            t.start();

+        }

+    }

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async1$1.class b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async1$1.class
new file mode 100644
index 0000000..531cadb
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async1$1.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async1.class b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async1.class
new file mode 100644
index 0000000..eb85ea0
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async1.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async1.java b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async1.java
new file mode 100644
index 0000000..9403a3a
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async1.java
@@ -0,0 +1,62 @@
+/*

+* 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 async;

+

+import java.io.IOException;

+

+import javax.servlet.AsyncContext;

+import javax.servlet.ServletException;

+import javax.servlet.http.HttpServlet;

+import javax.servlet.http.HttpServletRequest;

+import javax.servlet.http.HttpServletResponse;

+

+import org.apache.juli.logging.Log;

+import org.apache.juli.logging.LogFactory;

+

+public class Async1 extends HttpServlet {

+

+    private static final long serialVersionUID = 1L;

+

+    private static final Log log = LogFactory.getLog(Async1.class);

+

+    @Override

+    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

+        final AsyncContext actx = req.startAsync();

+        actx.setTimeout(30*1000);

+        Runnable run = new Runnable() {

+            @Override

+            public void run() {

+                try {

+                    String path = "/jsp/async/async1.jsp";

+                    Thread.currentThread().setName("Async1-Thread");

+                    log.info("Putting AsyncThread to sleep");

+                    Thread.sleep(2*1000);

+                    log.info("Dispatching to "+path);

+                    actx.dispatch(path);

+                }catch (InterruptedException x) {

+                    log.error("Async1",x);

+                }catch (IllegalStateException x) {

+                    log.error("Async1",x);

+                }

+            }

+        };

+        Thread t = new Thread(run);

+        t.start();

+    }

+

+

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async2$1.class b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async2$1.class
new file mode 100644
index 0000000..10cfa14
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async2$1.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async2.class b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async2.class
new file mode 100644
index 0000000..d79bb88
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async2.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async2.java b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async2.java
new file mode 100644
index 0000000..649afbc
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async2.java
@@ -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 async;

+

+import java.io.IOException;

+

+import javax.servlet.AsyncContext;

+import javax.servlet.ServletException;

+import javax.servlet.http.HttpServlet;

+import javax.servlet.http.HttpServletRequest;

+import javax.servlet.http.HttpServletResponse;

+

+import org.apache.juli.logging.Log;

+import org.apache.juli.logging.LogFactory;

+

+public class Async2 extends HttpServlet {

+

+    private static final long serialVersionUID = 1L;

+

+    private static final Log log = LogFactory.getLog(Async2.class);

+

+    @Override

+    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

+        final AsyncContext actx = req.startAsync();

+        actx.setTimeout(30*1000);

+        Runnable run = new Runnable() {

+            @Override

+            public void run() {

+                try {

+                    Thread.currentThread().setName("Async2-Thread");

+                    log.info("Putting AsyncThread to sleep");

+                    Thread.sleep(2*1000);

+                    log.info("Writing data.");

+                    actx.getResponse().getWriter().write("Output from background thread. Time:"+System.currentTimeMillis()+"\n");

+                    actx.complete();

+                }catch (InterruptedException x) {

+                    log.error("Async2",x);

+                }catch (IllegalStateException x) {

+                    log.error("Async2",x);

+                }catch (IOException x) {

+                    log.error("Async2",x);

+                }

+            }

+        };

+        Thread t = new Thread(run);

+        t.start();

+    }

+

+

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async3.class b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async3.class
new file mode 100644
index 0000000..bd90d90
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async3.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async3.java b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async3.java
new file mode 100644
index 0000000..39e0fe5
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Async3.java
@@ -0,0 +1,39 @@
+/*

+* 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 async;

+

+import java.io.IOException;

+

+import javax.servlet.AsyncContext;

+import javax.servlet.ServletException;

+import javax.servlet.http.HttpServlet;

+import javax.servlet.http.HttpServletRequest;

+import javax.servlet.http.HttpServletResponse;

+

+public class Async3 extends HttpServlet {

+

+    private static final long serialVersionUID = 1L;

+

+    @Override

+    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

+        final AsyncContext actx = req.startAsync();

+        actx.setTimeout(30*1000);

+        actx.dispatch("/jsp/async/async3.jsp");

+    }

+

+

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/async/AsyncStockServlet.class b/tomcat-uid/webapps/examples/WEB-INF/classes/async/AsyncStockServlet.class
new file mode 100644
index 0000000..117f084
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/async/AsyncStockServlet.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/async/AsyncStockServlet.java b/tomcat-uid/webapps/examples/WEB-INF/classes/async/AsyncStockServlet.java
new file mode 100644
index 0000000..ad4c43e
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/async/AsyncStockServlet.java
@@ -0,0 +1,131 @@
+/*

+ *  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 async;

+

+import java.io.IOException;

+import java.io.PrintWriter;

+import java.util.ArrayList;

+import java.util.Iterator;

+import java.util.concurrent.ConcurrentLinkedQueue;

+import java.util.concurrent.atomic.AtomicInteger;

+

+import javax.servlet.AsyncContext;

+import javax.servlet.AsyncEvent;

+import javax.servlet.AsyncListener;

+import javax.servlet.ServletException;

+import javax.servlet.http.HttpServlet;

+import javax.servlet.http.HttpServletRequest;

+import javax.servlet.http.HttpServletResponse;

+

+import async.Stockticker.Stock;

+import async.Stockticker.TickListener;

+

+public class AsyncStockServlet extends HttpServlet implements TickListener, AsyncListener{

+

+    private static final long serialVersionUID = 1L;

+

+    public static final String POLL = "POLL";

+    public static final String LONG_POLL = "LONG-POLL";

+    public static final String STREAM = "STREAM";

+

+    static ArrayList<Stock> ticks = new ArrayList<Stock>();

+    static ConcurrentLinkedQueue<AsyncContext> clients = new ConcurrentLinkedQueue<AsyncContext>();

+    static AtomicInteger clientcount = new AtomicInteger(0);

+    static Stockticker ticker = new Stockticker();

+

+    public AsyncStockServlet() {

+        System.out.println("AsyncStockServlet created");

+    }

+

+

+    @Override

+    protected void service(HttpServletRequest req, HttpServletResponse resp)

+            throws ServletException, IOException {

+        if (req.isAsyncStarted()) {

+            req.getAsyncContext().complete();

+        } else if (req.isAsyncSupported()) {

+            AsyncContext actx = req.startAsync();

+            actx.addListener(this);

+            resp.setContentType("text/plain");

+            clients.add(actx);

+            if (clientcount.incrementAndGet()==1) {

+                ticker.addTickListener(this);

+            }

+        } else {

+            new Exception("Async Not Supported").printStackTrace();

+            resp.sendError(400,"Async is not supported.");

+        }

+    }

+

+

+    @Override

+    public void tick(Stock stock) {

+        ticks.add((Stock)stock.clone());

+        Iterator<AsyncContext> it = clients.iterator();

+        while (it.hasNext()) {

+            AsyncContext actx = it.next();

+            try {

+                writeStock(actx, stock);

+            } catch (Exception e) {

+                // Ignore. The async error handling will deal with this.

+            }

+        }

+    }

+

+    public void writeStock(AsyncContext actx, Stock stock) {

+        HttpServletResponse response = (HttpServletResponse)actx.getResponse();

+        try {

+            PrintWriter writer = response.getWriter();

+            writer.write("STOCK#");//make client parsing easier

+            writer.write(stock.getSymbol());

+            writer.write("#");

+            writer.write(stock.getValueAsString());

+            writer.write("#");

+            writer.write(stock.getLastChangeAsString());

+            writer.write("#");

+            writer.write(String.valueOf(stock.getCnt()));

+            writer.write("\n");

+            writer.flush();

+            response.flushBuffer();

+        }catch (IOException x) {

+            try {actx.complete();}catch (Exception ignore){/* Ignore */}

+        }

+    }

+

+    @Override

+    public void onComplete(AsyncEvent event) throws IOException {

+        if (clients.remove(event.getAsyncContext()) && clientcount.decrementAndGet()==0) {

+            ticker.removeTickListener(this);

+        }

+    }

+

+    @Override

+    public void onError(AsyncEvent event) throws IOException {

+        event.getAsyncContext().complete();

+    }

+

+    @Override

+    public void onTimeout(AsyncEvent event) throws IOException {

+        event.getAsyncContext().complete();

+    }

+

+

+    @Override

+    public void onStartAsync(AsyncEvent event) throws IOException {

+        // NOOP

+    }

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/async/Stockticker$Stock.class b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Stockticker$Stock.class
new file mode 100644
index 0000000..bf771c3
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Stockticker$Stock.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/async/Stockticker$TickListener.class b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Stockticker$TickListener.class
new file mode 100644
index 0000000..928b52f
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Stockticker$TickListener.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/async/Stockticker.class b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Stockticker.class
new file mode 100644
index 0000000..7a99163
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Stockticker.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/async/Stockticker.java b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Stockticker.java
new file mode 100644
index 0000000..0bebf86
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/async/Stockticker.java
@@ -0,0 +1,188 @@
+/*

+ *  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 async;

+

+import java.text.DecimalFormat;

+import java.util.ArrayList;

+import java.util.Random;

+import java.util.concurrent.atomic.AtomicInteger;

+

+public class Stockticker implements Runnable {

+        public volatile boolean run = true;

+        protected AtomicInteger counter = new AtomicInteger(0);

+        ArrayList<TickListener> listeners = new ArrayList<TickListener>();

+        protected volatile Thread ticker = null;

+        protected volatile int ticknr = 0;

+

+        public synchronized void start() {

+            run = true;

+            ticker = new Thread(this);

+            ticker.setName("Ticker Thread");

+            ticker.start();

+        }

+

+        public synchronized void stop() {

+            run = false;

+            try {

+                ticker.join();

+            }catch (InterruptedException x) {

+                Thread.interrupted();

+            }

+

+            ticker = null;

+        }

+

+        public void addTickListener(TickListener listener) {

+            if (listeners.add(listener)) {

+                if (counter.incrementAndGet()==1) start();

+            }

+

+        }

+

+        public void removeTickListener(TickListener listener) {

+            if (listeners.remove(listener)) {

+                if (counter.decrementAndGet()==0) stop();

+            }

+        }

+

+        @Override

+        public void run() {

+            try {

+

+                Stock[] stocks = new Stock[] { new Stock("GOOG", 435.43),

+                        new Stock("YHOO", 27.88), new Stock("ASF", 1015.55), };

+                Random r = new Random(System.currentTimeMillis());

+                while (run) {

+                    for (int j = 0; j < 1; j++) {

+                        int i = r.nextInt() % 3;

+                        if (i < 0)

+                            i = i * (-1);

+                        Stock stock = stocks[i];

+                        double change = r.nextDouble();

+                        boolean plus = r.nextBoolean();

+                        if (plus) {

+                            stock.setValue(stock.getValue() + change);

+                        } else {

+                            stock.setValue(stock.getValue() - change);

+                        }

+                        stock.setCnt(++ticknr);

+                        for (TickListener l : listeners) {

+                            l.tick(stock);

+                        }

+

+                    }

+                    Thread.sleep(850);

+                }

+            } catch (InterruptedException ix) {

+                // Ignore

+            } catch (Exception x) {

+                x.printStackTrace();

+            }

+        }

+

+

+    public static interface TickListener {

+        public void tick(Stock stock);

+    }

+

+    public static final class Stock implements Cloneable {

+        protected static DecimalFormat df = new DecimalFormat("0.00");

+        protected String symbol = "";

+        protected double value = 0.0d;

+        protected double lastchange = 0.0d;

+        protected int cnt = 0;

+

+        public Stock(String symbol, double initvalue) {

+            this.symbol = symbol;

+            this.value = initvalue;

+        }

+

+        public void setCnt(int c) {

+            this.cnt = c;

+        }

+

+        public int getCnt() {

+            return cnt;

+        }

+

+        public String getSymbol() {

+            return symbol;

+        }

+

+        public double getValue() {

+            return value;

+        }

+

+        public void setValue(double value) {

+            double old = this.value;

+            this.value = value;

+            this.lastchange = value - old;

+        }

+

+        public String getValueAsString() {

+            return df.format(value);

+        }

+

+        public double getLastChange() {

+            return this.lastchange;

+        }

+

+        public void setLastChange(double lastchange) {

+            this.lastchange = lastchange;

+        }

+

+        public String getLastChangeAsString() {

+            return df.format(lastchange);

+        }

+

+        @Override

+        public int hashCode() {

+            return symbol.hashCode();

+        }

+

+        @Override

+        public boolean equals(Object other) {

+            if (other instanceof Stock) {

+                return this.symbol.equals(((Stock) other).symbol);

+            }

+

+            return false;

+        }

+

+        @Override

+        public String toString() {

+            StringBuilder buf = new StringBuilder("STOCK#");

+            buf.append(getSymbol());

+            buf.append("#");

+            buf.append(getValueAsString());

+            buf.append("#");

+            buf.append(getLastChangeAsString());

+            buf.append("#");

+            buf.append(String.valueOf(getCnt()));

+            return buf.toString();

+

+        }

+

+        @Override

+        public Object clone() {

+            Stock s = new Stock(this.getSymbol(), this.getValue());

+            s.setLastChange(this.getLastChange());

+            s.setCnt(this.cnt);

+            return s;

+        }

+    }

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/cal/Entries.class b/tomcat-uid/webapps/examples/WEB-INF/classes/cal/Entries.class
index ab44434..30bed2f 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/cal/Entries.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/cal/Entries.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/cal/Entries.java b/tomcat-uid/webapps/examples/WEB-INF/classes/cal/Entries.java
index ea0867b..443cd60 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/cal/Entries.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/cal/Entries.java
@@ -1,72 +1,60 @@
 /*

-* 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.

-*/

+ * 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 cal;

 

 import java.util.Hashtable;

-import javax.servlet.http.*;

+

+import javax.servlet.http.HttpServletRequest;

 

 public class Entries {

 

-  private Hashtable entries;

-  private static final String[] time = {"8am", "9am", "10am", "11am", "12pm", 

-					"1pm", "2pm", "3pm", "4pm", "5pm", "6pm",

-					"7pm", "8pm" };

-  public static final int rows = 12;

+    private Hashtable<String, Entry> entries;

+    private static final String[] time = { "8am", "9am", "10am", "11am",

+            "12pm", "1pm", "2pm", "3pm", "4pm", "5pm", "6pm", "7pm", "8pm" };

+    public static final int rows = 12;

 

-  public Entries () {   

-   entries = new Hashtable (rows);

-   for (int i=0; i < rows; i++) {

-     entries.put (time[i], new Entry(time[i]));

-   }

-  }

-

-  public int getRows () {

-    return rows;

-  }

-

-  public Entry getEntry (int index) {

-    return (Entry)this.entries.get(time[index]);

-  }

-

-  public int getIndex (String tm) {

-    for (int i=0; i<rows; i++)

-      if(tm.equals(time[i])) return i;

-    return -1;

-  }

-

-  public void processRequest (HttpServletRequest request, String tm) {

-    int index = getIndex (tm);

-    if (index >= 0) {

-      String descr = request.getParameter ("description");

-      ((Entry)entries.get(time[index])).setDescription (descr);

+    public Entries() {

+        entries = new Hashtable<String, Entry>(rows);

+        for (int i = 0; i < rows; i++) {

+            entries.put(time[i], new Entry(time[i]));

+        }

     }

-  }

+

+    public int getRows() {

+        return rows;

+    }

+

+    public Entry getEntry(int index) {

+        return this.entries.get(time[index]);

+    }

+

+    public int getIndex(String tm) {

+        for (int i = 0; i < rows; i++)

+            if (tm.equals(time[i]))

+                return i;

+        return -1;

+    }

+

+    public void processRequest(HttpServletRequest request, String tm) {

+        int index = getIndex(tm);

+        if (index >= 0) {

+            String descr = request.getParameter("description");

+            entries.get(time[index]).setDescription(descr);

+        }

+    }

 

 }

-

-

-

-

-

-

-

-

-

-

-

-

-

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/cal/Entry.class b/tomcat-uid/webapps/examples/WEB-INF/classes/cal/Entry.class
index b4619a6..44c493d 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/cal/Entry.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/cal/Entry.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/cal/Entry.java b/tomcat-uid/webapps/examples/WEB-INF/classes/cal/Entry.java
index d4596d9..e6403b2 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/cal/Entry.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/cal/Entry.java
@@ -1,55 +1,53 @@
 /*

-* 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.

-*/

+ * 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 cal;

 

 public class Entry {

 

-  String hour;

-  String description;

-  String color;

+    String hour;

+    String description;

 

-  public Entry (String hour) {

-    this.hour = hour;

-    this.description = "";

+    public Entry(String hour) {

+        this.hour = hour;

+        this.description = "";

 

-  }

+    }

 

-  public String getHour () {

-    return this.hour;

-  }

+    public String getHour() {

+        return this.hour;

+    }

 

-  public String getColor () {

-    if (description.equals("")) return "lightblue";

-    else return "red";

-  }

+    public String getColor() {

+        if (description.equals("")) {

+            return "lightblue";

+        }

+        return "red";

+    }

 

-  public String getDescription () {

-    if (description.equals("")) return "None";

-    else return this.description;

-  }

+    public String getDescription() {

+        if (description.equals("")) {

+            return "None";

+        }

+        return this.description;

+    }

 

-  public void setDescription (String descr) {

-    description = descr;

-  }

- 

+    public void setDescription(String descr) {

+        description = descr;

+    }

+

 }

-

-

-

-

-

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/cal/JspCalendar.class b/tomcat-uid/webapps/examples/WEB-INF/classes/cal/JspCalendar.class
index dfd95e1..e1c6efa 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/cal/JspCalendar.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/cal/JspCalendar.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/cal/JspCalendar.java b/tomcat-uid/webapps/examples/WEB-INF/classes/cal/JspCalendar.java
index b2db6c0..dff319c 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/cal/JspCalendar.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/cal/JspCalendar.java
@@ -17,138 +17,135 @@
 

 package cal;

 

-import java.util.*;

+import java.util.Calendar;

+import java.util.Date;

 

 public class JspCalendar {

     Calendar  calendar = null;

-    Date currentDate;

 

     public JspCalendar() {

-	calendar = Calendar.getInstance();

-	Date trialTime = new Date();

-	calendar.setTime(trialTime);

+        calendar = Calendar.getInstance();

+        Date trialTime = new Date();

+        calendar.setTime(trialTime);

     }

 

 

     public int getYear() {

-	return calendar.get(Calendar.YEAR);

+        return calendar.get(Calendar.YEAR);

     }

-    

+

     public String getMonth() {

-	int m = getMonthInt();

-	String[] months = new String [] { "January", "February", "March",

-					"April", "May", "June",

-					"July", "August", "September",

-					"October", "November", "December" };

-	if (m > 12)

-	    return "Unknown to Man";

-	

-	return months[m - 1];

+        int m = getMonthInt();

+        String[] months = new String [] { "January", "February", "March",

+                                        "April", "May", "June",

+                                        "July", "August", "September",

+                                        "October", "November", "December" };

+        if (m > 12)

+            return "Unknown to Man";

+

+        return months[m - 1];

 

     }

 

     public String getDay() {

-	int x = getDayOfWeek();

-	String[] days = new String[] {"Sunday", "Monday", "Tuesday", "Wednesday", 

-				      "Thursday", "Friday", "Saturday"};

+        int x = getDayOfWeek();

+        String[] days = new String[] {"Sunday", "Monday", "Tuesday", "Wednesday",

+                                      "Thursday", "Friday", "Saturday"};

 

-	if (x > 7)

-	    return "Unknown to Man";

+        if (x > 7)

+            return "Unknown to Man";

 

-	return days[x - 1];

+        return days[x - 1];

 

     }

-    

+

     public int getMonthInt() {

-	return 1 + calendar.get(Calendar.MONTH);

+        return 1 + calendar.get(Calendar.MONTH);

     }

 

     public String getDate() {

-	return getMonthInt() + "/" + getDayOfMonth() + "/" +  getYear();	

+        return getMonthInt() + "/" + getDayOfMonth() + "/" +  getYear();

     }

 

     public String getCurrentDate() {

         Date dt = new Date ();

-	calendar.setTime (dt);

-	return getMonthInt() + "/" + getDayOfMonth() + "/" +  getYear();

+        calendar.setTime (dt);

+        return getMonthInt() + "/" + getDayOfMonth() + "/" +  getYear();

 

     }

 

     public String getNextDate() {

         calendar.set (Calendar.DAY_OF_MONTH, getDayOfMonth() + 1);

-	return getDate ();

+        return getDate ();

     }

 

     public String getPrevDate() {

         calendar.set (Calendar.DAY_OF_MONTH, getDayOfMonth() - 1);

-	return getDate ();

+        return getDate ();

     }

 

     public String getTime() {

-	return getHour() + ":" + getMinute() + ":" + getSecond();

+        return getHour() + ":" + getMinute() + ":" + getSecond();

     }

 

     public int getDayOfMonth() {

-	return calendar.get(Calendar.DAY_OF_MONTH);

+        return calendar.get(Calendar.DAY_OF_MONTH);

     }

 

     public int getDayOfYear() {

-	return calendar.get(Calendar.DAY_OF_YEAR);

+        return calendar.get(Calendar.DAY_OF_YEAR);

     }

 

     public int getWeekOfYear() {

-	return calendar.get(Calendar.WEEK_OF_YEAR);

+        return calendar.get(Calendar.WEEK_OF_YEAR);

     }

 

     public int getWeekOfMonth() {

-	return calendar.get(Calendar.WEEK_OF_MONTH);

+        return calendar.get(Calendar.WEEK_OF_MONTH);

     }

 

     public int getDayOfWeek() {

-	return calendar.get(Calendar.DAY_OF_WEEK);

+        return calendar.get(Calendar.DAY_OF_WEEK);

     }

-     

+

     public int getHour() {

-	return calendar.get(Calendar.HOUR_OF_DAY);

+        return calendar.get(Calendar.HOUR_OF_DAY);

     }

-    

+

     public int getMinute() {

-	return calendar.get(Calendar.MINUTE);

+        return calendar.get(Calendar.MINUTE);

     }

 

 

     public int getSecond() {

-	return calendar.get(Calendar.SECOND);

+        return calendar.get(Calendar.SECOND);

     }

 

-  

+

     public int getEra() {

-	return calendar.get(Calendar.ERA);

+        return calendar.get(Calendar.ERA);

     }

 

     public String getUSTimeZone() {

-	String[] zones = new String[] {"Hawaii", "Alaskan", "Pacific",

-				       "Mountain", "Central", "Eastern"};

-	

-	return zones[10 + getZoneOffset()];

+        String[] zones = new String[] {"Hawaii", "Alaskan", "Pacific",

+                                       "Mountain", "Central", "Eastern"};

+

+        return zones[10 + getZoneOffset()];

     }

 

     public int getZoneOffset() {

-	return calendar.get(Calendar.ZONE_OFFSET)/(60*60*1000);

+        return calendar.get(Calendar.ZONE_OFFSET)/(60*60*1000);

     }

 

 

     public int getDSTOffset() {

-	return calendar.get(Calendar.DST_OFFSET)/(60*60*1000);

+        return calendar.get(Calendar.DST_OFFSET)/(60*60*1000);

     }

 

-    

+

     public int getAMPM() {

-	return calendar.get(Calendar.AM_PM);

+        return calendar.get(Calendar.AM_PM);

     }

 }

 

 

-

-

-

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/cal/TableBean.class b/tomcat-uid/webapps/examples/WEB-INF/classes/cal/TableBean.class
index 8afd547..fdd37a8 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/cal/TableBean.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/cal/TableBean.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/cal/TableBean.java b/tomcat-uid/webapps/examples/WEB-INF/classes/cal/TableBean.java
index f55327c..1c32e68 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/cal/TableBean.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/cal/TableBean.java
@@ -1,100 +1,101 @@
 /*

-* 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.

-*/

+ * 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 cal;

 

-import javax.servlet.http.*;

 import java.util.Hashtable;

 

+import javax.servlet.http.HttpServletRequest;

+

 public class TableBean {

 

-  Hashtable table;

-  JspCalendar JspCal;

-  Entries entries;

-  String date;

-  String name = null;

-  String email = null;

-  boolean processError = false;

+    Hashtable<String, Entries> table;

+    JspCalendar JspCal;

+    Entries entries;

+    String date;

+    String name = null;

+    String email = null;

+    boolean processError = false;

 

-  public TableBean () {

-    this.table = new Hashtable (10);

-    this.JspCal = new JspCalendar ();

-    this.date = JspCal.getCurrentDate ();

-  }

-

-  public void setName (String nm) {

-    this.name = nm;

-  }

-

-  public String getName () {

-    return this.name;

-  }

-  

-  public void setEmail (String mail) {

-    this.email = mail;

-  }

-

-  public String getEmail () {

-    return this.email;

-  }

-

-  public String getDate () {

-    return this.date;

-  }

-

-  public Entries getEntries () {

-    return this.entries;

-  }

-

-  public void processRequest (HttpServletRequest request) {

-

-    // Get the name and e-mail.

-    this.processError = false;

-    if (name == null || name.equals("")) setName(request.getParameter ("name"));  

-    if (email == null || email.equals("")) setEmail(request.getParameter ("email"));

-    if (name == null || email == null ||

-		name.equals("") || email.equals("")) {

-      this.processError = true;

-      return;

+    public TableBean() {

+        this.table = new Hashtable<String, Entries>(10);

+        this.JspCal = new JspCalendar();

+        this.date = JspCal.getCurrentDate();

     }

 

-    // Get the date.

-    String dateR = request.getParameter ("date");

-    if (dateR == null) date = JspCal.getCurrentDate ();

-    else if (dateR.equalsIgnoreCase("next")) date = JspCal.getNextDate ();

-    else if (dateR.equalsIgnoreCase("prev")) date = JspCal.getPrevDate ();

-

-    entries = (Entries) table.get (date);

-    if (entries == null) {

-      entries = new Entries ();

-      table.put (date, entries);

+    public void setName(String nm) {

+        this.name = nm;

     }

 

-    // If time is provided add the event.

-	String time = request.getParameter("time");

-    if (time != null) entries.processRequest (request, time);

-  }

+    public String getName() {

+        return this.name;

+    }

 

-  public boolean getProcessError () {

-    return this.processError;

-  }

+    public void setEmail(String mail) {

+        this.email = mail;

+    }

+

+    public String getEmail() {

+        return this.email;

+    }

+

+    public String getDate() {

+        return this.date;

+    }

+

+    public Entries getEntries() {

+        return this.entries;

+    }

+

+    public void processRequest(HttpServletRequest request) {

+

+        // Get the name and e-mail.

+        this.processError = false;

+        if (name == null || name.equals(""))

+            setName(request.getParameter("name"));

+        if (email == null || email.equals(""))

+            setEmail(request.getParameter("email"));

+        if (name == null || email == null || name.equals("")

+                || email.equals("")) {

+            this.processError = true;

+            return;

+        }

+

+        // Get the date.

+        String dateR = request.getParameter("date");

+        if (dateR == null)

+            date = JspCal.getCurrentDate();

+        else if (dateR.equalsIgnoreCase("next"))

+            date = JspCal.getNextDate();

+        else if (dateR.equalsIgnoreCase("prev"))

+            date = JspCal.getPrevDate();

+

+        entries = table.get(date);

+        if (entries == null) {

+            entries = new Entries();

+            table.put(date, entries);

+        }

+

+        // If time is provided add the event.

+        String time = request.getParameter("time");

+        if (time != null)

+            entries.processRequest(request, time);

+    }

+

+    public boolean getProcessError() {

+        return this.processError;

+    }

 }

-

-

-

-

-

-

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/chat/ChatServlet$MessageSender.class b/tomcat-uid/webapps/examples/WEB-INF/classes/chat/ChatServlet$MessageSender.class
index 6712d68..c4be561 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/chat/ChatServlet$MessageSender.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/chat/ChatServlet$MessageSender.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/chat/ChatServlet.class b/tomcat-uid/webapps/examples/WEB-INF/classes/chat/ChatServlet.class
index 752adba..ea047bd 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/chat/ChatServlet.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/chat/ChatServlet.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/chat/ChatServlet.java b/tomcat-uid/webapps/examples/WEB-INF/classes/chat/ChatServlet.java
index 11d3757..d92092d 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/chat/ChatServlet.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/chat/ChatServlet.java
@@ -5,9 +5,9 @@
  * 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.

@@ -24,14 +24,14 @@
 import java.io.PrintWriter;

 import java.util.ArrayList;

 

-import org.apache.catalina.CometEvent;

-import org.apache.catalina.CometProcessor;

-

 import javax.servlet.ServletException;

 import javax.servlet.http.HttpServlet;

 import javax.servlet.http.HttpServletRequest;

 import javax.servlet.http.HttpServletResponse;

 

+import org.apache.catalina.comet.CometEvent;

+import org.apache.catalina.comet.CometProcessor;

+

 

 /**

  * Helper class to implement Comet functionality.

@@ -39,20 +39,24 @@
 public class ChatServlet

     extends HttpServlet implements CometProcessor {

 

+    private static final long serialVersionUID = 1L;

+

     private static final String CHARSET = "UTF-8";

 

-    protected ArrayList<HttpServletResponse> connections = 

+    protected ArrayList<HttpServletResponse> connections =

         new ArrayList<HttpServletResponse>();

-    protected MessageSender messageSender = null;

-    

+    protected transient MessageSender messageSender = null;

+

+    @Override

     public void init() throws ServletException {

         messageSender = new MessageSender();

-        Thread messageSenderThread = 

+        Thread messageSenderThread =

             new Thread(messageSender, "MessageSender[" + getServletContext().getContextPath() + "]");

         messageSenderThread.setDaemon(true);

         messageSenderThread.start();

     }

 

+    @Override

     public void destroy() {

         connections.clear();

         messageSender.stop();

@@ -61,11 +65,12 @@
 

     /**

      * Process the given Comet event.

-     * 

+     *

      * @param event The Comet event that will be processed

      * @throws IOException

      * @throws ServletException

      */

+    @Override

     public void event(CometEvent event)

         throws IOException, ServletException {

 

@@ -73,7 +78,7 @@
         // mixing Comet stuff with regular connection processing

         HttpServletRequest request = event.getHttpServletRequest();

         HttpServletResponse response = event.getHttpServletResponse();

-        

+

         if (event.getEventType() == CometEvent.EventType.BEGIN) {

             String action = request.getParameter("action");

             if (action != null) {

@@ -83,22 +88,20 @@
                     response.sendRedirect("index.jsp");

                     event.close();

                     return;

-                } else {

-                    String nickname = (String) request.getSession(true).getAttribute("nickname");

-                    String message = request.getParameter("message");

-                    messageSender.send(nickname, message);

-                    response.sendRedirect("post.jsp");

-                    event.close();

-                    return;

                 }

-            } else {

-                if (request.getSession(true).getAttribute("nickname") == null) {

-                    // Redirect to "login"

-                    log("Redirect to login for session: " + request.getSession(true).getId());

-                    response.sendRedirect("login.jsp");

-                    event.close();

-                    return;

-                }

+                String nickname = (String) request.getSession(true).getAttribute("nickname");

+                String message = request.getParameter("message");

+                messageSender.send(nickname, message);

+                response.sendRedirect("post.jsp");

+                event.close();

+                return;

+            }

+            if (request.getSession(true).getAttribute("nickname") == null) {

+                // Redirect to "login"

+                log("Redirect to login for session: " + request.getSession(true).getId());

+                response.sendRedirect("login.jsp");

+                event.close();

+                return;

             }

             begin(event, request, response);

         } else if (event.getEventType() == CometEvent.EventType.ERROR) {

@@ -110,8 +113,9 @@
         }

     }

 

-    protected void begin(CometEvent event, HttpServletRequest request, HttpServletResponse response)

-        throws IOException, ServletException {

+    protected void begin(@SuppressWarnings("unused") CometEvent event,

+            HttpServletRequest request, HttpServletResponse response)

+        throws IOException {

         log("Begin for session: " + request.getSession(true).getId());

 

         response.setContentType("text/html; charset=" + CHARSET);

@@ -128,38 +132,38 @@
 

         messageSender.send("Tomcat", request.getSession(true).getAttribute("nickname") + " joined the chat.");

     }

-    

+

     protected void end(CometEvent event, HttpServletRequest request, HttpServletResponse response)

-        throws IOException, ServletException {

+        throws IOException {

         log("End for session: " + request.getSession(true).getId());

         synchronized(connections) {

             connections.remove(response);

         }

-        

+

         PrintWriter writer = response.getWriter();

         writer.println("</body></html>");

-        

+

         event.close();

     }

-    

+

     protected void error(CometEvent event, HttpServletRequest request, HttpServletResponse response)

-        throws IOException, ServletException {

+        throws IOException {

         log("Error for session: " + request.getSession(true).getId());

         synchronized(connections) {

             connections.remove(response);

         }

         event.close();

     }

-    

+

     protected void read(CometEvent event, HttpServletRequest request, HttpServletResponse response)

-        throws IOException, ServletException {

+        throws IOException {

         InputStream is = request.getInputStream();

         byte[] buf = new byte[512];

         while (is.available() > 0) {

             log("Available: " + is.available());

             int n = is.read(buf);

             if (n > 0) {

-                log("Read " + n + " bytes: " + new String(buf, 0, n) 

+                log("Read " + n + " bytes: " + new String(buf, 0, n)

                         + " for session: " + request.getSession(true).getId());

             } else if (n < 0) {

                 log("End of file: " + n);

@@ -169,6 +173,7 @@
         }

     }

 

+    @Override

     protected void service(HttpServletRequest request, HttpServletResponse response)

         throws IOException, ServletException {

         // Compatibility method: equivalent method using the regular connection model

@@ -180,7 +185,7 @@
         writer.println("Configure a connector that supports Comet and try again.");

         writer.println("</body></html>");

     }

-    

+

 

     /**

      * Poller class.

@@ -189,10 +194,11 @@
 

         protected boolean running = true;

         protected ArrayList<String> messages = new ArrayList<String>();

-        

+

         public MessageSender() {

+            // Default contructor

         }

-        

+

         public void stop() {

             running = false;

             synchronized (messages) {

@@ -200,14 +206,6 @@
             }

         }

 

-        /**

-         * Add specified socket and associated pool to the poller. The socket will

-         * be added to a temporary array, and polled first after a maximum amount

-         * of time equal to pollTime (in most cases, latency will be much lower,

-         * however).

-         *

-         * @param socket to add to the poller

-         */

         public void send(String user, String message) {

             synchronized (messages) {

                 messages.add("[" + user + "]: " + message);

@@ -219,6 +217,7 @@
          * The background thread that listens for incoming TCP/IP connections and

          * hands them off to an appropriate processor.

          */

+        @Override

         public void run() {

 

             // Loop until we receive a shutdown command

@@ -226,7 +225,7 @@
                 String[] pendingMessages;

                 synchronized (messages) {

                     try {

-                        if (messages.size() == 0) {

+                        if (running && messages.size() == 0) {

                             messages.wait();

                         }

                     } catch (InterruptedException e) {

@@ -261,7 +260,7 @@
      * in HTML.

      *

      * @param message The message string to be filtered

-     * @author Copied from org.apache.catalina.util.RequestUtil#filter(String) 

+     * @author Copied from org.apache.catalina.util.RequestUtil#filter(String)

      */

     protected static String filter(String message) {

         if (message == null)

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/checkbox/CheckTest.class b/tomcat-uid/webapps/examples/WEB-INF/classes/checkbox/CheckTest.class
index 87011a7..e5fc31c 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/checkbox/CheckTest.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/checkbox/CheckTest.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/checkbox/CheckTest.java b/tomcat-uid/webapps/examples/WEB-INF/classes/checkbox/CheckTest.java
index 70623e2..c25448b 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/checkbox/CheckTest.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/checkbox/CheckTest.java
@@ -22,10 +22,10 @@
     String b[] = new String[] { "1", "2", "3", "4" };

 

     public String[] getFruit() {

-	return b;

+        return b;

     }

 

     public void setFruit(String [] b) {

-	this.b = b;

+        this.b = b;

     }

 }

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/colors/ColorGameBean.class b/tomcat-uid/webapps/examples/WEB-INF/classes/colors/ColorGameBean.class
index 408dc45..bed4bf3 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/colors/ColorGameBean.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/colors/ColorGameBean.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/colors/ColorGameBean.java b/tomcat-uid/webapps/examples/WEB-INF/classes/colors/ColorGameBean.java
index 5748217..6e2d741 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/colors/ColorGameBean.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/colors/ColorGameBean.java
@@ -16,8 +16,6 @@
 */

 package colors;

 

-import javax.servlet.http.*;

-

 public class ColorGameBean {

 

     private String background = "yellow";

@@ -26,90 +24,90 @@
     private String color2 = background;

     private String hint = "no";

     private int attempts = 0;

-	private int intval = 0;

+        private int intval = 0;

     private boolean tookHints = false;

 

-    public void processRequest(HttpServletRequest request) {

+    public void processRequest() {

 

-	// background = "yellow";

-	// foreground = "red";

+        // background = "yellow";

+        // foreground = "red";

 

-	if (! color1.equals(foreground)) {

-	    if (color1.equalsIgnoreCase("black") ||

-			color1.equalsIgnoreCase("cyan")) {

-			background = color1;

-		}

-	}

+        if (! color1.equals(foreground)) {

+            if (color1.equalsIgnoreCase("black") ||

+                        color1.equalsIgnoreCase("cyan")) {

+                        background = color1;

+                }

+        }

 

-	if (! color2.equals(background)) {

-	    if (color2.equalsIgnoreCase("black") ||

-			color2.equalsIgnoreCase("cyan")) {

-			foreground = color2;

-	    }

-	}

+        if (! color2.equals(background)) {

+            if (color2.equalsIgnoreCase("black") ||

+                        color2.equalsIgnoreCase("cyan")) {

+                        foreground = color2;

+            }

+        }

 

-	attempts++;

+        attempts++;

     }

 

     public void setColor2(String x) {

-	color2 = x;

+        color2 = x;

     }

 

     public void setColor1(String x) {

-	color1 = x;

+        color1 = x;

     }

 

     public void setAction(String x) {

-	if (!tookHints)

-	    tookHints = x.equalsIgnoreCase("Hint");

-	hint = x;

+        if (!tookHints)

+            tookHints = x.equalsIgnoreCase("Hint");

+        hint = x;

     }

 

     public String getColor2() {

-	 return background;

+         return background;

     }

 

     public String getColor1() {

-	 return foreground;

+         return foreground;

     }

 

     public int getAttempts() {

-	return attempts;

+        return attempts;

     }

 

     public boolean getHint() {

-	return hint.equalsIgnoreCase("Hint");

+        return hint.equalsIgnoreCase("Hint");

     }

 

     public boolean getSuccess() {

-	if (background.equalsIgnoreCase("black") ||

-	    background.equalsIgnoreCase("cyan")) {

-	

-	    if (foreground.equalsIgnoreCase("black") ||

-		foreground.equalsIgnoreCase("cyan"))

-		return true;

-	    else

-		return false;

-	}

+        if (background.equalsIgnoreCase("black") ||

+            background.equalsIgnoreCase("cyan")) {

 

-	return false;

+            if (foreground.equalsIgnoreCase("black") ||

+                foreground.equalsIgnoreCase("cyan")) {

+                return true;

+            }

+            return false;

+        }

+

+        return false;

     }

 

     public boolean getHintTaken() {

-	return tookHints;

+        return tookHints;

     }

 

     public void reset() {

-	foreground = "red";

-	background = "yellow";

+        foreground = "red";

+        background = "yellow";

     }

 

     public void setIntval(int value) {

-	intval = value;

-	}

+        intval = value;

+        }

 

     public int getIntval() {

-	return intval;

-	}

+        return intval;

+        }

 }

 

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilter.class b/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilter.class
index 619a1a2..f81aba7 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilter.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilter.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilter.java b/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilter.java
index fc298e2..350393a 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilter.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilter.java
@@ -14,11 +14,14 @@
 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

-

 package compressionFilters;

 

 import java.io.IOException;

+import java.util.ArrayList;

 import java.util.Enumeration;

+import java.util.List;

+import java.util.StringTokenizer;

+

 import javax.servlet.Filter;

 import javax.servlet.FilterChain;

 import javax.servlet.FilterConfig;

@@ -28,7 +31,6 @@
 import javax.servlet.http.HttpServletRequest;

 import javax.servlet.http.HttpServletResponse;

 

-

 /**

  * Implementation of <code>javax.servlet.Filter</code> used to compress

  * the ServletResponse if it is bigger than a threshold.

@@ -36,7 +38,7 @@
  * @author Amy Roh

  * @author Dmitri Valdin

  */

-public class CompressionFilter implements Filter{

+public class CompressionFilter implements Filter {

 

     /**

      * The filter configuration object we are associated with.  If this value

@@ -45,18 +47,32 @@
     private FilterConfig config = null;

 

     /**

-     * Minimal reasonable threshold

+     * Minimal reasonable threshold.

      */

     private int minThreshold = 128;

 

-

     /**

-     * The threshold number to compress

+     * The threshold number to compress.

      */

-    protected int compressionThreshold;

+    protected int compressionThreshold = 0;

 

     /**

-     * Debug level for this filter

+     * Minimal reasonable buffer.

+     */

+    private int minBuffer = 8192;  // 8KB is what tomcat would use by default anyway

+

+    /**

+     * The compression buffer size to avoid chunking.

+     */

+    protected int compressionBuffer = 0;

+

+    /**

+     * The mime types to compress.

+     */

+    protected String[] compressionMimeTypes = {"text/html", "text/xml", "text/plain"};

+

+    /**

+     * Debug level for this filter.

      */

     private int debug = 0;

 

@@ -65,7 +81,7 @@
      *

      * @param filterConfig The filter configuration object

      */

-

+    @Override

     public void init(FilterConfig filterConfig) {

 

         config = filterConfig;

@@ -73,9 +89,8 @@
             String value = filterConfig.getInitParameter("debug");

             if (value!=null) {

                 debug = Integer.parseInt(value);

-            } else {

-                debug = 0;

             }

+

             String str = filterConfig.getInitParameter("compressionThreshold");

             if (str!=null) {

                 compressionThreshold = Integer.parseInt(str);

@@ -86,12 +101,43 @@
                     }

                     compressionThreshold = minThreshold;

                 }

-            } else {

-                compressionThreshold = 0;

             }

 

-        } else {

-            compressionThreshold = 0;

+            str = filterConfig.getInitParameter("compressionBuffer");

+            if (str!=null) {

+                compressionBuffer = Integer.parseInt(str);

+                if (compressionBuffer < minBuffer) {

+                    if (debug > 0) {

+                        System.out.println("compressionBuffer should be >= " + minBuffer);

+                        System.out.println("compressionBuffer set to " + minBuffer);

+                    }

+                    compressionBuffer = minBuffer;

+                }

+            }

+

+            str = filterConfig.getInitParameter("compressionMimeTypes");

+            if (str!=null) {

+                List<String> values = new ArrayList<String>();

+                StringTokenizer st = new StringTokenizer(str, ",");

+

+                while (st.hasMoreTokens()) {

+                    String token = st.nextToken().trim();

+                    if (token.length() > 0) {

+                        values.add(token);

+                    }

+                }

+

+                if (values.size() > 0) {

+                    compressionMimeTypes = values.toArray(

+                            new String[values.size()]);

+                } else {

+                    compressionMimeTypes = null;

+                }

+

+                if (debug > 0) {

+                    System.out.println("compressionMimeTypes set to " + compressionMimeTypes);

+                }

+            }

         }

 

     }

@@ -99,6 +145,7 @@
     /**

     * Take this filter out of service.

     */

+    @Override

     public void destroy() {

 

         this.config = null;

@@ -121,7 +168,7 @@
      * It then invokes the next entity in the chain using the FilterChain object

      * (<code>chain.doFilter()</code>), <br>

      **/

-

+    @Override

     public void doFilter ( ServletRequest request, ServletResponse response,

                         FilterChain chain ) throws IOException, ServletException {

 

@@ -131,7 +178,7 @@
 

         if (compressionThreshold == 0) {

             if (debug > 0) {

-                System.out.println("doFilter gets called, but compressionTreshold is set to 0 - no compression");

+                System.out.println("doFilter got called, but compressionThreshold is set to 0 - no compression");

             }

             chain.doFilter(request, response);

             return;

@@ -144,7 +191,7 @@
             }

 

             // Are we allowed to compress ?

-            String s = (String) ((HttpServletRequest)request).getParameter("gzip");

+            String s = ((HttpServletRequest)request).getParameter("gzip");

             if ("false".equals(s)) {

                 if (debug > 0) {

                     System.out.println("got parameter gzip=false --> don't compress, just chain filter");

@@ -153,10 +200,10 @@
                 return;

             }

 

-            Enumeration e =

+            Enumeration<String> e =

                 ((HttpServletRequest)request).getHeaders("Accept-Encoding");

             while (e.hasMoreElements()) {

-                String name = (String)e.nextElement();

+                String name = e.nextElement();

                 if (name.indexOf("gzip") != -1) {

                     if (debug > 0) {

                         System.out.println("supports compression");

@@ -164,24 +211,20 @@
                     supportCompression = true;

                 } else {

                     if (debug > 0) {

-                        System.out.println("no support for compresion");

+                        System.out.println("no support for compression");

                     }

                 }

             }

         }

 

-        if (!supportCompression) {

-            if (debug > 0) {

-                System.out.println("doFilter gets called wo compression");

-            }

-            chain.doFilter(request, response);

-            return;

-        } else {

+        if (supportCompression) {

             if (response instanceof HttpServletResponse) {

                 CompressionServletResponseWrapper wrappedResponse =

                     new CompressionServletResponseWrapper((HttpServletResponse)response);

                 wrappedResponse.setDebugLevel(debug);

                 wrappedResponse.setCompressionThreshold(compressionThreshold);

+                wrappedResponse.setCompressionBuffer(compressionBuffer);

+                wrappedResponse.setCompressionMimeTypes(compressionMimeTypes);

                 if (debug > 0) {

                     System.out.println("doFilter gets called with compression");

                 }

@@ -192,6 +235,12 @@
                 }

                 return;

             }

+        } else {

+            if (debug > 0) {

+                System.out.println("doFilter gets called w/o compression");

+            }

+            chain.doFilter(request, response);

+            return;

         }

     }

 

@@ -206,12 +255,12 @@
     }

 

     /**

-     * Return filter config

      * Required by Weblogic 6.1

+     *

+     * @return the FilterConfig that was used to initialise this filter.

      */

     public FilterConfig getFilterConfig() {

         return config;

     }

-

 }

 

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilterTestServlet.class b/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilterTestServlet.class
index a48332d..ce695cb 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilterTestServlet.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilterTestServlet.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilterTestServlet.java b/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilterTestServlet.java
index 9f6090a..700cd1f 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilterTestServlet.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilterTestServlet.java
@@ -14,13 +14,16 @@
 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

-

 package compressionFilters;

 

 import java.io.IOException;

 import java.util.Enumeration;

-import javax.servlet.*;

-import javax.servlet.http.*;

+

+import javax.servlet.ServletException;

+import javax.servlet.ServletOutputStream;

+import javax.servlet.http.HttpServlet;

+import javax.servlet.http.HttpServletRequest;

+import javax.servlet.http.HttpServletResponse;

 

 /**

  * Very Simple test servlet to test compression filter

@@ -28,15 +31,18 @@
  */

 public class CompressionFilterTestServlet extends HttpServlet {

 

+    private static final long serialVersionUID = 1L;

+

+    @Override

     public void doGet(HttpServletRequest request, HttpServletResponse response)

         throws ServletException, IOException {

 

         ServletOutputStream out = response.getOutputStream();

         response.setContentType("text/plain");

 

-        Enumeration e = ((HttpServletRequest)request).getHeaders("Accept-Encoding");

+        Enumeration<String> e = request.getHeaders("Accept-Encoding");

         while (e.hasMoreElements()) {

-            String name = (String)e.nextElement();

+            String name = e.nextElement();

             out.println(name);

             if (name.indexOf("gzip") != -1) {

                 out.println("gzip supported -- able to compress");

@@ -48,6 +54,11 @@
 

 

         out.println("Compression Filter Test Servlet");

+        out.println("Minimum content length for compression is 128 bytes");

+        out.println("**********  32 bytes  **********");

+        out.println("**********  32 bytes  **********");

+        out.println("**********  32 bytes  **********");

+        out.println("**********  32 bytes  **********");

         out.close();

     }

 

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.class b/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.class
index a52f332..3d05f19 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.java b/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.java
index 120b82a..a79027a 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.java
@@ -19,9 +19,8 @@
 import java.io.IOException;

 import java.io.OutputStream;

 import java.util.zip.GZIPOutputStream;

-import javax.servlet.ServletOutputStream;

-import javax.servlet.http.HttpServletResponse;

 

+import javax.servlet.ServletOutputStream;

 

 /**

  * Implementation of <b>ServletOutputStream</b> that works with

@@ -30,25 +29,24 @@
  * @author Amy Roh

  * @author Dmitri Valdin

  */

-public class CompressionResponseStream

-    extends ServletOutputStream {

-

+public class CompressionResponseStream extends ServletOutputStream {

 

     // ----------------------------------------------------------- Constructors

 

-

     /**

      * Construct a servlet output stream associated with the specified Response.

      *

-     * @param response The associated response

+     * @param responseWrapper The associated response wrapper

+     * @param originalOutput the output stream

      */

-    public CompressionResponseStream(HttpServletResponse response) throws IOException{

+    public CompressionResponseStream(

+            CompressionServletResponseWrapper responseWrapper,

+            ServletOutputStream originalOutput) {

 

         super();

         closed = false;

-        this.response = response;

-        this.output = response.getOutputStream();

-

+        this.response = responseWrapper;

+        this.output = originalOutput;

     }

 

 

@@ -62,6 +60,16 @@
     protected int compressionThreshold = 0;

 

     /**

+     * The compression buffer size to avoid chunking

+     */

+    protected int compressionBuffer = 0;

+

+    /**

+     * The mime types to compress

+     */

+    protected String[] compressionMimeTypes = {"text/html", "text/xml", "text/plain"};

+

+    /**

      * Debug level

      */

     private int debug = 0;

@@ -95,10 +103,10 @@
     /**

      * The response with which this servlet output stream is associated.

      */

-    protected HttpServletResponse response = null;

+    protected CompressionServletResponseWrapper response = null;

 

     /**

-     * The underlying servket output stream to which we should write data.

+     * The underlying servlet output stream to which we should write data.

      */

     protected ServletOutputStream output = null;

 

@@ -116,11 +124,31 @@
     /**

      * Set the compressionThreshold number and create buffer for this size

      */

-    protected void setBuffer(int threshold) {

-        compressionThreshold = threshold;

-        buffer = new byte[compressionThreshold];

+    protected void setCompressionThreshold(int compressionThreshold) {

+        this.compressionThreshold = compressionThreshold;

+        buffer = new byte[this.compressionThreshold];

         if (debug > 1) {

-            System.out.println("buffer is set to "+compressionThreshold);

+            System.out.println("compressionThreshold is set to "+ this.compressionThreshold);

+        }

+    }

+

+    /**

+     * The compression buffer size to avoid chunking

+     */

+    protected void setCompressionBuffer(int compressionBuffer) {

+        this.compressionBuffer = compressionBuffer;

+        if (debug > 1) {

+            System.out.println("compressionBuffer is set to "+ this.compressionBuffer);

+        }

+    }

+

+    /**

+     * Set supported mime types

+     */

+    public void setCompressionMimeTypes(String[] compressionMimeTypes) {

+        this.compressionMimeTypes = compressionMimeTypes;

+        if (debug > 1) {

+            System.out.println("compressionMimeTypes is set to " + this.compressionMimeTypes);

         }

     }

 

@@ -128,6 +156,7 @@
      * Close this output stream, causing any buffered data to be flushed and

      * any further output data to throw an IOException.

      */

+    @Override

     public void close() throws IOException {

 

         if (debug > 1) {

@@ -162,6 +191,7 @@
      * Flush any buffered data for this output stream, which also causes the

      * response to be committed.

      */

+    @Override

     public void flush() throws IOException {

 

         if (debug > 1) {

@@ -199,6 +229,7 @@
      *

      * @exception IOException if an input/output error occurs

      */

+    @Override

     public void write(int b) throws IOException {

 

         if (debug > 1) {

@@ -224,6 +255,7 @@
      *

      * @exception IOException if an input/output error occurs

      */

+    @Override

     public void write(byte b[]) throws IOException {

 

         write(b, 0, b.length);

@@ -241,6 +273,7 @@
      *

      * @exception IOException if an input/output error occurs

      */

+    @Override

     public void write(byte b[], int off, int len) throws IOException {

 

         if (debug > 1) {

@@ -293,12 +326,53 @@
             if (debug > 1) {

                 System.out.println("new GZIPOutputStream");

             }

+

+            boolean alreadyCompressed = false;

+            String contentEncoding = response.getHeader("Content-Encoding");

+            if (contentEncoding != null) {

+                if (contentEncoding.contains("gzip")) {

+                    alreadyCompressed = true;

+                    if (debug > 0) {

+                        System.out.println("content is already compressed");

+                    }

+                } else {

+                    if (debug > 0) {

+                        System.out.println("content is not compressed yet");

+                    }

+                }

+            }

+

+            boolean compressibleMimeType = false;

+            // Check for compatible MIME-TYPE

+            if (compressionMimeTypes != null) {

+                if (startsWithStringArray(compressionMimeTypes, response.getContentType())) {

+                    compressibleMimeType = true;

+                    if (debug > 0) {

+                        System.out.println("mime type " + response.getContentType() + " is compressible");

+                    }

+                } else {

+                    if (debug > 0) {

+                        System.out.println("mime type " + response.getContentType() + " is not compressible");

+                    }

+                }

+            }

+

             if (response.isCommitted()) {

                 if (debug > 1)

                     System.out.print("Response already committed. Using original output stream");

                 gzipstream = output;

+            } else if (alreadyCompressed) {

+                if (debug > 1)

+                    System.out.print("Response already compressed. Using original output stream");

+                gzipstream = output;

+            } else if (!compressibleMimeType) {

+                if (debug > 1)

+                    System.out.print("Response mime type is not compressible. Using original output stream");

+                gzipstream = output;

             } else {

                 response.addHeader("Content-Encoding", "gzip");

+                response.setContentLength(-1);  // don't use any preset content-length as it will be wrong after gzipping

+                response.setBufferSize(compressionBuffer);

                 gzipstream = new GZIPOutputStream(output);

             }

         }

@@ -319,4 +393,20 @@
 

     }

 

+    /**

+     * Checks if any entry in the string array starts with the specified value

+     *

+     * @param sArray the StringArray

+     * @param value string

+     */

+    private boolean startsWithStringArray(String sArray[], String value) {

+        if (value == null)

+           return false;

+        for (int i = 0; i < sArray.length; i++) {

+            if (value.startsWith(sArray[i])) {

+                return true;

+            }

+        }

+        return false;

+    }

 }

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.class b/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.class
index 838e08c..1198e42 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.java b/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.java
index e2cd66b..1e28d44 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.java
@@ -19,6 +19,9 @@
 import java.io.IOException;

 import java.io.OutputStreamWriter;

 import java.io.PrintWriter;

+import java.util.HashMap;

+import java.util.Map;

+

 import javax.servlet.ServletOutputStream;

 import javax.servlet.http.HttpServletResponse;

 import javax.servlet.http.HttpServletResponseWrapper;

@@ -30,7 +33,8 @@
  * @author Amy Roh

  * @author Dmitri Valdin

  */

-public class CompressionServletResponseWrapper extends HttpServletResponseWrapper {

+public class CompressionServletResponseWrapper

+        extends HttpServletResponseWrapper {

 

     // ----------------------------------------------------- Constructor

 

@@ -38,7 +42,6 @@
      * Calls the parent constructor which creates a ServletResponse adaptor

      * wrapping the given response object.

      */

-

     public CompressionServletResponseWrapper(HttpServletResponse response) {

         super(response);

         origResponse = response;

@@ -80,43 +83,61 @@
     /**

      * The threshold number to compress

      */

-    protected int threshold = 0;

+    protected int compressionThreshold = 0;

+

+    /**

+     * The compression buffer size

+     */

+    protected int compressionBuffer = 8192;  // 8KB default

+

+    /**

+     * The mime types to compress

+     */

+    protected String[] compressionMimeTypes = {"text/html", "text/xml", "text/plain"};

 

     /**

      * Debug level

      */

-    private int debug = 0;

+    protected int debug = 0;

 

     /**

-     * Content type

+     * keeps a copy of all headers set

      */

-    protected String contentType = null;

+    private Map<String,String> headerCopies = new HashMap<String,String>();

+

 

     // --------------------------------------------------------- Public Methods

 

 

     /**

-     * Set content type

-     */

-    public void setContentType(String contentType) {

-        if (debug > 1) {

-            System.out.println("setContentType to "+contentType);

-        }

-        this.contentType = contentType;

-        origResponse.setContentType(contentType);

-    }

-

-

-    /**

      * Set threshold number

      */

     public void setCompressionThreshold(int threshold) {

         if (debug > 1) {

             System.out.println("setCompressionThreshold to " + threshold);

         }

-        this.threshold = threshold;

+        this.compressionThreshold = threshold;

     }

 

+    /**

+     * Set compression buffer

+     */

+    public void setCompressionBuffer(int buffer) {

+        if (debug > 1) {

+            System.out.println("setCompressionBuffer to " + buffer);

+        }

+        this.compressionBuffer = buffer;

+    }

+

+    /**

+     * Set compressible mime types

+     */

+    public void setCompressionMimeTypes(String[] mimeTypes) {

+        if (debug > 1) {

+            System.out.println("setCompressionMimeTypes to " + mimeTypes);

+        }

+        this.compressionMimeTypes = mimeTypes;

+    }

 

     /**

      * Set debug level

@@ -137,12 +158,14 @@
             System.out.println("createOutputStream gets called");

         }

 

-        CompressionResponseStream stream = new CompressionResponseStream(origResponse);

+        CompressionResponseStream stream = new CompressionResponseStream(

+                this, origResponse.getOutputStream());

         stream.setDebugLevel(debug);

-        stream.setBuffer(threshold);

+        stream.setCompressionThreshold(compressionThreshold);

+        stream.setCompressionBuffer(compressionBuffer);

+        stream.setCompressionMimeTypes(compressionMimeTypes);

 

         return stream;

-

     }

 

 

@@ -158,6 +181,7 @@
                     stream.close();

             }

         } catch (IOException e) {

+            // Ignore

         }

     }

 

@@ -170,9 +194,10 @@
      *

      * @exception IOException if an input/output error occurs

      */

+    @Override

     public void flushBuffer() throws IOException {

         if (debug > 1) {

-            System.out.println("flush buffer @ CompressionServletResponseWrapper");

+            System.out.println("flush buffer @ GZipServletResponseWrapper");

         }

         ((CompressionResponseStream)stream).flush();

 

@@ -185,6 +210,7 @@
      *  already been called for this response

      * @exception IOException if an input/output error occurs

      */

+    @Override

     public ServletOutputStream getOutputStream() throws IOException {

 

         if (writer != null)

@@ -207,6 +233,7 @@
      *  already been called for this response

      * @exception IOException if an input/output error occurs

      */

+    @Override

     public PrintWriter getWriter() throws IOException {

 

         if (writer != null)

@@ -219,7 +246,6 @@
         if (debug > 1) {

             System.out.println("stream is set to "+stream+" in getWriter");

         }

-        //String charset = getCharsetFromContentType(contentType);

         String charEnc = origResponse.getCharacterEncoding();

         if (debug > 1) {

             System.out.println("character encoding is " + charEnc);

@@ -231,13 +257,29 @@
         } else {

             writer = new PrintWriter(stream);

         }

-        

+

         return (writer);

+    }

 

+    @Override

+    public String getHeader(String name) {

+        return headerCopies.get(name);

+    }

+

+    @Override

+    public void addHeader(String name, String value) {

+        if (headerCopies.containsKey(name)) {

+            String existingValue = headerCopies.get(name);

+            if ((existingValue != null) && (existingValue.length() > 0)) headerCopies.put(name, existingValue + "," + value);

+            else headerCopies.put(name, value);

+        } else headerCopies.put(name, value);

+        super.addHeader(name, value);

     }

 

 

-    public void setContentLength(int length) {

+    @Override

+    public void setHeader(String name, String value) {

+        headerCopies.put(name, value);

+        super.setHeader(name, value);

     }

-

 }

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/dates/JspCalendar.class b/tomcat-uid/webapps/examples/WEB-INF/classes/dates/JspCalendar.class
index 7ae66d3..56cd2b5 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/dates/JspCalendar.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/dates/JspCalendar.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/dates/JspCalendar.java b/tomcat-uid/webapps/examples/WEB-INF/classes/dates/JspCalendar.java
index 22e1f86..9c90bbd 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/dates/JspCalendar.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/dates/JspCalendar.java
@@ -16,137 +16,138 @@
 */

 package dates;

 

-import java.util.*;

+import java.util.Calendar;

+import java.util.Date;

 

 public class JspCalendar {

     Calendar  calendar = null;

 

     public JspCalendar() {

-	calendar = Calendar.getInstance();

-	Date trialTime = new Date();

-	calendar.setTime(trialTime);

+        calendar = Calendar.getInstance();

+        Date trialTime = new Date();

+        calendar.setTime(trialTime);

     }

 

     public int getYear() {

-	return calendar.get(Calendar.YEAR);

+        return calendar.get(Calendar.YEAR);

     }

-    

+

     public String getMonth() {

-	int m = getMonthInt();

-	String[] months = new String [] { "January", "February", "March",

-					"April", "May", "June",

-					"July", "August", "September",

-					"October", "November", "December" };

-	if (m > 12)

-	    return "Unknown to Man";

-	

-	return months[m - 1];

+        int m = getMonthInt();

+        String[] months = new String [] { "January", "February", "March",

+                                        "April", "May", "June",

+                                        "July", "August", "September",

+                                        "October", "November", "December" };

+        if (m > 12)

+            return "Unknown to Man";

+

+        return months[m - 1];

 

     }

 

     public String getDay() {

-	int x = getDayOfWeek();

-	String[] days = new String[] {"Sunday", "Monday", "Tuesday", "Wednesday", 

-				      "Thursday", "Friday", "Saturday"};

+        int x = getDayOfWeek();

+        String[] days = new String[] {"Sunday", "Monday", "Tuesday", "Wednesday",

+                                      "Thursday", "Friday", "Saturday"};

 

-	if (x > 7)

-	    return "Unknown to Man";

+        if (x > 7)

+            return "Unknown to Man";

 

-	return days[x - 1];

+        return days[x - 1];

 

     }

-    

+

     public int getMonthInt() {

-	return 1 + calendar.get(Calendar.MONTH);

+        return 1 + calendar.get(Calendar.MONTH);

     }

 

     public String getDate() {

-	return getMonthInt() + "/" + getDayOfMonth() + "/" +  getYear();

+        return getMonthInt() + "/" + getDayOfMonth() + "/" +  getYear();

 

     }

 

     public String getTime() {

-	return getHour() + ":" + getMinute() + ":" + getSecond();

+        return getHour() + ":" + getMinute() + ":" + getSecond();

     }

 

     public int getDayOfMonth() {

-	return calendar.get(Calendar.DAY_OF_MONTH);

+        return calendar.get(Calendar.DAY_OF_MONTH);

     }

 

     public int getDayOfYear() {

-	return calendar.get(Calendar.DAY_OF_YEAR);

+        return calendar.get(Calendar.DAY_OF_YEAR);

     }

 

     public int getWeekOfYear() {

-	return calendar.get(Calendar.WEEK_OF_YEAR);

+        return calendar.get(Calendar.WEEK_OF_YEAR);

     }

 

     public int getWeekOfMonth() {

-	return calendar.get(Calendar.WEEK_OF_MONTH);

+        return calendar.get(Calendar.WEEK_OF_MONTH);

     }

 

     public int getDayOfWeek() {

-	return calendar.get(Calendar.DAY_OF_WEEK);

+        return calendar.get(Calendar.DAY_OF_WEEK);

     }

-     

+

     public int getHour() {

-	return calendar.get(Calendar.HOUR_OF_DAY);

+        return calendar.get(Calendar.HOUR_OF_DAY);

     }

-    

+

     public int getMinute() {

-	return calendar.get(Calendar.MINUTE);

+        return calendar.get(Calendar.MINUTE);

     }

 

 

     public int getSecond() {

-	return calendar.get(Calendar.SECOND);

+        return calendar.get(Calendar.SECOND);

     }

 

     public static void main(String args[]) {

-	JspCalendar db = new JspCalendar();

-	p("date: " + db.getDayOfMonth());

-	p("year: " + db.getYear());

-	p("month: " + db.getMonth());

-	p("time: " + db.getTime());

-	p("date: " + db.getDate());

-	p("Day: " + db.getDay());

-	p("DayOfYear: " + db.getDayOfYear());

-	p("WeekOfYear: " + db.getWeekOfYear());

-	p("era: " + db.getEra());

-	p("ampm: " + db.getAMPM());

-	p("DST: " + db.getDSTOffset());

-	p("ZONE Offset: " + db.getZoneOffset());

-	p("TIMEZONE: " + db.getUSTimeZone());

+        JspCalendar db = new JspCalendar();

+        p("date: " + db.getDayOfMonth());

+        p("year: " + db.getYear());

+        p("month: " + db.getMonth());

+        p("time: " + db.getTime());

+        p("date: " + db.getDate());

+        p("Day: " + db.getDay());

+        p("DayOfYear: " + db.getDayOfYear());

+        p("WeekOfYear: " + db.getWeekOfYear());

+        p("era: " + db.getEra());

+        p("ampm: " + db.getAMPM());

+        p("DST: " + db.getDSTOffset());

+        p("ZONE Offset: " + db.getZoneOffset());

+        p("TIMEZONE: " + db.getUSTimeZone());

     }

 

     private static void p(String x) {

-	System.out.println(x);

+        System.out.println(x);

     }

 

 

     public int getEra() {

-	return calendar.get(Calendar.ERA);

+        return calendar.get(Calendar.ERA);

     }

 

     public String getUSTimeZone() {

-	String[] zones = new String[] {"Hawaii", "Alaskan", "Pacific",

-				       "Mountain", "Central", "Eastern"};

-	

-	return zones[10 + getZoneOffset()];

+        String[] zones = new String[] {"Hawaii", "Alaskan", "Pacific",

+                                       "Mountain", "Central", "Eastern"};

+

+        return zones[10 + getZoneOffset()];

     }

 

     public int getZoneOffset() {

-	return calendar.get(Calendar.ZONE_OFFSET)/(60*60*1000);

+        return calendar.get(Calendar.ZONE_OFFSET)/(60*60*1000);

     }

 

 

     public int getDSTOffset() {

-	return calendar.get(Calendar.DST_OFFSET)/(60*60*1000);

+        return calendar.get(Calendar.DST_OFFSET)/(60*60*1000);

     }

 

-    

+

     public int getAMPM() {

-	return calendar.get(Calendar.AM_PM);

+        return calendar.get(Calendar.AM_PM);

     }

 }

 

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/error/Smart.class b/tomcat-uid/webapps/examples/WEB-INF/classes/error/Smart.class
index 5445742..fd96ee2 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/error/Smart.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/error/Smart.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/error/Smart.java b/tomcat-uid/webapps/examples/WEB-INF/classes/error/Smart.java
index 3af3a38..67aff03 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/error/Smart.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/error/Smart.java
@@ -1,32 +1,30 @@
 /*

-* 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.

-*/

-

-

+ * 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 error;

 

 public class Smart {

 

-  String name = "JSP";

+    String name = "JSP";

 

-  public String getName () {

-	return name;

-  }	

+    public String getName() {

+        return name;

+    }

 

-  public void setName (String name) {

-	this.name = name;

-  }	

+    public void setName(String name) {

+        this.name = name;

+    }

 }

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/examples/ExampleTagBase.class b/tomcat-uid/webapps/examples/WEB-INF/classes/examples/ExampleTagBase.class
index 35edd28..4ac9330 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/examples/ExampleTagBase.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/examples/ExampleTagBase.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/examples/ExampleTagBase.java b/tomcat-uid/webapps/examples/WEB-INF/classes/examples/ExampleTagBase.java
index 13bcd16..d339adb 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/examples/ExampleTagBase.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/examples/ExampleTagBase.java
@@ -16,52 +16,59 @@
 */

 package examples;

 

-import javax.servlet.jsp.*;

-import javax.servlet.jsp.tagext.*;

+import javax.servlet.jsp.JspException;

+import javax.servlet.jsp.tagext.BodyContent;

+import javax.servlet.jsp.tagext.BodyTagSupport;

+import javax.servlet.jsp.tagext.Tag;

 

 public abstract class ExampleTagBase extends BodyTagSupport {

 

+    private static final long serialVersionUID = 1L;

+

+    @Override

     public void setParent(Tag parent) {

         this.parent = parent;

     }

 

+    @Override

     public void setBodyContent(BodyContent bodyOut) {

         this.bodyOut = bodyOut;

     }

 

-    public void setPageContext(PageContext pageContext) {

-        this.pageContext = pageContext;

-    }

-

+    @Override

     public Tag getParent() {

         return this.parent;

     }

-    

+

+    @Override

     public int doStartTag() throws JspException {

         return SKIP_BODY;

     }

 

+    @Override

     public int doEndTag() throws JspException {

         return EVAL_PAGE;

     }

-    

 

-    // Default implementations for BodyTag methods as well

-    // just in case a tag decides to implement BodyTag.

+

+    @Override

     public void doInitBody() throws JspException {

+        // Default implementations for BodyTag methods as well

+        // just in case a tag decides to implement BodyTag.

     }

 

+    @Override

     public int doAfterBody() throws JspException {

         return SKIP_BODY;

     }

 

+    @Override

     public void release() {

         bodyOut = null;

         pageContext = null;

         parent = null;

     }

-    

+

     protected BodyContent bodyOut;

-    protected PageContext pageContext;

     protected Tag parent;

 }

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/examples/FooTag.class b/tomcat-uid/webapps/examples/WEB-INF/classes/examples/FooTag.class
index d60a6bd..3d6d760 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/examples/FooTag.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/examples/FooTag.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/examples/FooTag.java b/tomcat-uid/webapps/examples/WEB-INF/classes/examples/FooTag.java
index 4e11f30..c8fdb0a 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/examples/FooTag.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/examples/FooTag.java
@@ -16,9 +16,11 @@
 */

 package examples;

 

-import javax.servlet.jsp.*;

 import java.io.IOException;

 

+import javax.servlet.jsp.JspException;

+import javax.servlet.jsp.JspTagException;

+

 /**

  * Example1: the simplest tag

  * Collect attributes and call into some actions

@@ -26,20 +28,21 @@
  * <foo att1="..." att2="...." att3="...." />

  */

 

-public class FooTag 

-    extends ExampleTagBase 

-{

+public class FooTag extends ExampleTagBase {

+

+    private static final long serialVersionUID = 1L;

+

     private String atts[] = new String[3];

     int i = 0;

-    

+

     private final void setAtt(int index, String value) {

         atts[index] = value;

     }

-    

+

     public void setAtt1(String value) {

         setAtt(0, value);

     }

-    

+

     public void setAtt2(String value) {

         setAtt(1, value);

     }

@@ -47,29 +50,33 @@
     public void setAtt3(String value) {

         setAtt(2, value);

     }

-    

+

     /**

      * Process start tag

      *

      * @return EVAL_BODY_INCLUDE

      */

+    @Override

     public int doStartTag() throws JspException {

         i = 0;

-	return EVAL_BODY_BUFFERED;

+        return EVAL_BODY_BUFFERED;

     }

 

+    @Override

     public void doInitBody() throws JspException {

         pageContext.setAttribute("member", atts[i]);

         i++;

     }

-    

+

+    @Override

     public int doAfterBody() throws JspException {

         try {

             if (i == 3) {

                 bodyOut.writeOut(bodyOut.getEnclosingWriter());

                 return SKIP_BODY;

-            } else

-                pageContext.setAttribute("member", atts[i]);

+            }

+

+            pageContext.setAttribute("member", atts[i]);

             i++;

             return EVAL_BODY_BUFFERED;

         } catch (IOException ex) {

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/examples/FooTagExtraInfo.class b/tomcat-uid/webapps/examples/WEB-INF/classes/examples/FooTagExtraInfo.class
index 5ca9cfb..ead843e 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/examples/FooTagExtraInfo.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/examples/FooTagExtraInfo.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/examples/FooTagExtraInfo.java b/tomcat-uid/webapps/examples/WEB-INF/classes/examples/FooTagExtraInfo.java
index 99e5dfb..1ae0492 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/examples/FooTagExtraInfo.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/examples/FooTagExtraInfo.java
@@ -16,11 +16,14 @@
 */

 package examples;

 

-import javax.servlet.jsp.tagext.*;

+import javax.servlet.jsp.tagext.TagData;

+import javax.servlet.jsp.tagext.TagExtraInfo;

+import javax.servlet.jsp.tagext.VariableInfo;

 

 public class FooTagExtraInfo extends TagExtraInfo {

+    @Override

     public VariableInfo[] getVariableInfo(TagData data) {

-        return new VariableInfo[] 

+        return new VariableInfo[]

             {

                 new VariableInfo("member",

                                  "String",

@@ -30,4 +33,4 @@
     }

 }

 

-        

+

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/examples/LogTag.class b/tomcat-uid/webapps/examples/WEB-INF/classes/examples/LogTag.class
index 92abeab..7b62ca8 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/examples/LogTag.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/examples/LogTag.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/examples/LogTag.java b/tomcat-uid/webapps/examples/WEB-INF/classes/examples/LogTag.java
index 961cfc8..32584fd 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/examples/LogTag.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/examples/LogTag.java
@@ -16,19 +16,20 @@
 */

 package examples;

 

-

-import javax.servlet.jsp.*;

-

 import java.io.IOException;

 

+import javax.servlet.jsp.JspException;

+import javax.servlet.jsp.JspTagException;

+

 /**

- * Log the contents of the body. Could be used to handle errors etc. 

+ * Log the contents of the body. Could be used to handle errors etc.

  */

-public class LogTag 

-    extends ExampleTagBase

-{

+public class LogTag extends ExampleTagBase {

+

+    private static final long serialVersionUID = 1L;

+

     boolean toBrowser = false;

-    

+

     public void setToBrowser(String value) {

         if (value == null)

             toBrowser = false;

@@ -38,10 +39,12 @@
             toBrowser = false;

     }

 

+    @Override

     public int doStartTag() throws JspException {

         return EVAL_BODY_BUFFERED;

     }

-    

+

+    @Override

     public int doAfterBody() throws JspException {

         try {

             String s = bodyOut.getString();

@@ -55,6 +58,4 @@
     }

 }

 

-    

-        

-    

+

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/examples/ShowSource.class b/tomcat-uid/webapps/examples/WEB-INF/classes/examples/ShowSource.class
index c02b3ff..5e4fba6 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/examples/ShowSource.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/examples/ShowSource.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/examples/ShowSource.java b/tomcat-uid/webapps/examples/WEB-INF/classes/examples/ShowSource.java
index 8afbf09..195e32d 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/examples/ShowSource.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/examples/ShowSource.java
@@ -16,43 +16,45 @@
 */

 package examples;

 

+import java.io.IOException;

+import java.io.InputStream;

+import java.util.Locale;

 

-import javax.servlet.jsp.*;

-import javax.servlet.jsp.tagext.*;

-

-import java.io.*;

+import javax.servlet.jsp.JspException;

+import javax.servlet.jsp.JspTagException;

+import javax.servlet.jsp.JspWriter;

+import javax.servlet.jsp.tagext.TagSupport;

 

 /**

  * Display the sources of the JSP file.

  */

-public class ShowSource

-    extends TagSupport

-{

+public class ShowSource extends TagSupport {

+

+    private static final long serialVersionUID = 1L;

+

     String jspFile;

-    

+

     public void setJspFile(String jspFile) {

         this.jspFile = jspFile;

     }

 

+    @Override

     public int doEndTag() throws JspException {

-	if ((jspFile.indexOf( ".." ) >= 0) ||

-            (jspFile.toUpperCase().indexOf("/WEB-INF/") != 0) ||

-            (jspFile.toUpperCase().indexOf("/META-INF/") != 0))

-	    throw new JspTagException("Invalid JSP file " + jspFile);

+        if ((jspFile.indexOf( ".." ) >= 0) ||

+            (jspFile.toUpperCase(Locale.ENGLISH).indexOf("/WEB-INF/") != 0) ||

+            (jspFile.toUpperCase(Locale.ENGLISH).indexOf("/META-INF/") != 0))

+            throw new JspTagException("Invalid JSP file " + jspFile);

 

-        InputStream in

-            = pageContext.getServletContext().getResourceAsStream(jspFile);

-

+        InputStream in = pageContext.getServletContext().getResourceAsStream(

+                jspFile);

         if (in == null)

-            throw new JspTagException("Unable to find JSP file: "+jspFile);

-

-        JspWriter out = pageContext.getOut();

-

+            throw new JspTagException("Unable to find JSP file: " + jspFile);

 

         try {

+            JspWriter out = pageContext.getOut();

             out.println("<body>");

             out.println("<pre>");

-            for(int ch = in.read(); ch != -1; ch = in.read())

+            for (int ch = in.read(); ch != -1; ch = in.read())

                 if (ch == '<')

                     out.print("&lt;");

                 else

@@ -60,12 +62,16 @@
             out.println("</pre>");

             out.println("</body>");

         } catch (IOException ex) {

-            throw new JspTagException("IOException: "+ex.toString());

+            throw new JspTagException("IOException: " + ex.toString());

+        } finally {

+            try {

+                in.close();

+            } catch (IOException e) {

+                throw new JspTagException("Can't close inputstream: ", e);

+            }

         }

         return super.doEndTag();

     }

 }

 

-    

-        

-    

+

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/examples/ValuesTag.class b/tomcat-uid/webapps/examples/WEB-INF/classes/examples/ValuesTag.class
new file mode 100644
index 0000000..cc4d3ff
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/examples/ValuesTag.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/examples/ValuesTag.java b/tomcat-uid/webapps/examples/WEB-INF/classes/examples/ValuesTag.java
new file mode 100644
index 0000000..20468dc
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/examples/ValuesTag.java
@@ -0,0 +1,79 @@
+/*

+ * 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 examples;

+

+import java.io.IOException;

+

+import javax.servlet.jsp.JspException;

+import javax.servlet.jsp.JspTagException;

+import javax.servlet.jsp.JspWriter;

+import javax.servlet.jsp.tagext.TagSupport;

+

+/**

+ * Accept and display a value.

+ */

+public class ValuesTag extends TagSupport {

+

+    private static final long serialVersionUID = 1L;

+

+    // Using "-1" as the default value,

+    // in the assumption that it won't be used as the value.

+    // Cannot use null here, because null is an important case

+    // that should be present in the tests.

+    private Object objectValue = "-1";

+    private String stringValue = "-1";

+    private long longValue = -1;

+    private double doubleValue = -1;

+

+    public void setObject(Object objectValue) {

+        this.objectValue = objectValue;

+    }

+

+    public void setString(String stringValue) {

+        this.stringValue = stringValue;

+    }

+

+    public void setLong(long longValue) {

+        this.longValue = longValue;

+    }

+

+    public void setDouble(double doubleValue) {

+        this.doubleValue = doubleValue;

+    }

+

+    @Override

+    public int doEndTag() throws JspException {

+        JspWriter out = pageContext.getOut();

+

+        try {

+            if (!"-1".equals(objectValue)) {

+                out.print(objectValue);

+            } else if (!"-1".equals(stringValue)) {

+                out.print(stringValue);

+            } else if (longValue != -1) {

+                out.print(longValue);

+            } else if (doubleValue != -1) {

+                out.print(doubleValue);

+            } else {

+                out.print("-1");

+            }

+        } catch (IOException ex) {

+            throw new JspTagException("IOException: " + ex.toString(), ex);

+        }

+        return super.doEndTag();

+    }

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/filters/ExampleFilter.class b/tomcat-uid/webapps/examples/WEB-INF/classes/filters/ExampleFilter.class
index 10a6710..12e41d8 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/filters/ExampleFilter.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/filters/ExampleFilter.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/filters/ExampleFilter.java b/tomcat-uid/webapps/examples/WEB-INF/classes/filters/ExampleFilter.java
index 0601b87..59764ef 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/filters/ExampleFilter.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/filters/ExampleFilter.java
@@ -19,6 +19,7 @@
 

 

 import java.io.IOException;

+

 import javax.servlet.Filter;

 import javax.servlet.FilterChain;

 import javax.servlet.FilterConfig;

@@ -67,6 +68,7 @@
     /**

      * Take this filter out of service.

      */

+    @Override

     public void destroy() {

 

         this.attribute = null;

@@ -80,27 +82,28 @@
      * current filter stack, including the ultimately invoked servlet.

      *

      * @param request The servlet request we are processing

-     * @param result The servlet response we are creating

+     * @param response The servlet response we are creating

      * @param chain The filter chain we are processing

      *

      * @exception IOException if an input/output error occurs

      * @exception ServletException if a servlet error occurs

      */

+    @Override

     public void doFilter(ServletRequest request, ServletResponse response,

                          FilterChain chain)

-	throws IOException, ServletException {

+        throws IOException, ServletException {

 

-	// Store ourselves as a request attribute (if requested)

-	if (attribute != null)

-	    request.setAttribute(attribute, this);

+        // Store ourselves as a request attribute (if requested)

+        if (attribute != null)

+            request.setAttribute(attribute, this);

 

-	// Time and log the subsequent processing

-	long startTime = System.currentTimeMillis();

+        // Time and log the subsequent processing

+        long startTime = System.currentTimeMillis();

         chain.doFilter(request, response);

-	long stopTime = System.currentTimeMillis();

-	filterConfig.getServletContext().log

-	    (this.toString() + ": " + (stopTime - startTime) +

-	     " milliseconds");

+        long stopTime = System.currentTimeMillis();

+        filterConfig.getServletContext().log

+            (this.toString() + ": " + (stopTime - startTime) +

+             " milliseconds");

 

     }

 

@@ -108,12 +111,13 @@
     /**

      * Place this filter into service.

      *

-     * @param filterConfig The filter configuration object

+     * @param fConfig The filter configuration object

      */

-    public void init(FilterConfig filterConfig) throws ServletException {

+    @Override

+    public void init(FilterConfig fConfig) throws ServletException {

 

-	this.filterConfig = filterConfig;

-        this.attribute = filterConfig.getInitParameter("attribute");

+        this.filterConfig = fConfig;

+        this.attribute = fConfig.getInitParameter("attribute");

 

     }

 

@@ -121,14 +125,15 @@
     /**

      * Return a String representation of this object.

      */

+    @Override

     public String toString() {

 

-	if (filterConfig == null)

-	    return ("InvokerFilter()");

-	StringBuffer sb = new StringBuffer("InvokerFilter(");

-	sb.append(filterConfig);

-	sb.append(")");

-	return (sb.toString());

+        if (filterConfig == null)

+            return ("TimingFilter()");

+        StringBuilder sb = new StringBuilder("TimingFilter(");

+        sb.append(filterConfig);

+        sb.append(")");

+        return (sb.toString());

 

     }

 

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/filters/RequestDumperFilter.class b/tomcat-uid/webapps/examples/WEB-INF/classes/filters/RequestDumperFilter.class
deleted file mode 100644
index 5b244bf..0000000
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/filters/RequestDumperFilter.class
+++ /dev/null
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/filters/RequestDumperFilter.java b/tomcat-uid/webapps/examples/WEB-INF/classes/filters/RequestDumperFilter.java
deleted file mode 100644
index 7c45a40..0000000
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/filters/RequestDumperFilter.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*

-* 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 filters;

-

-

-import java.io.IOException;

-import java.io.PrintWriter;

-import java.io.StringWriter;

-import java.sql.Timestamp;

-import java.util.Enumeration;

-import java.util.Locale;

-import javax.servlet.Filter;

-import javax.servlet.FilterChain;

-import javax.servlet.FilterConfig;

-import javax.servlet.ServletException;

-import javax.servlet.ServletRequest;

-import javax.servlet.ServletResponse;

-import javax.servlet.http.Cookie;

-import javax.servlet.http.HttpServletRequest;

-

-

-/**

- * Example filter that dumps interesting state information about a request

- * to the associated servlet context log file, before allowing the servlet

- * to process the request in the usual way.  This can be installed as needed

- * to assist in debugging problems.

- *

- * @author Craig McClanahan

- */

-public final class RequestDumperFilter implements Filter {

-

-

-    // ----------------------------------------------------- Instance Variables

-

-

-    /**

-     * The filter configuration object we are associated with.  If this value

-     * is null, this filter instance is not currently configured.

-     */

-    private FilterConfig filterConfig = null;

-

-

-    // --------------------------------------------------------- Public Methods

-

-

-    /**

-     * Take this filter out of service.

-     */

-    public void destroy() {

-

-        this.filterConfig = null;

-

-    }

-

-

-    /**

-     * Time the processing that is performed by all subsequent filters in the

-     * current filter stack, including the ultimately invoked servlet.

-     *

-     * @param request The servlet request we are processing

-     * @param result The servlet response we are creating

-     * @param chain The filter chain we are processing

-     *

-     * @exception IOException if an input/output error occurs

-     * @exception ServletException if a servlet error occurs

-     */

-    public void doFilter(ServletRequest request, ServletResponse response,

-                         FilterChain chain)

-	throws IOException, ServletException {

-

-        if (filterConfig == null)

-	    return;

-

-	// Render the generic servlet request properties

-	StringWriter sw = new StringWriter();

-	PrintWriter writer = new PrintWriter(sw);

-	writer.println("Request Received at " +

-		       (new Timestamp(System.currentTimeMillis())));

-	writer.println(" characterEncoding=" + request.getCharacterEncoding());

-	writer.println("     contentLength=" + request.getContentLength());

-	writer.println("       contentType=" + request.getContentType());

-	writer.println("            locale=" + request.getLocale());

-	writer.print("           locales=");

-	Enumeration locales = request.getLocales();

-	boolean first = true;

-	while (locales.hasMoreElements()) {

-	    Locale locale = (Locale) locales.nextElement();

-	    if (first)

-	        first = false;

-	    else

-	        writer.print(", ");

-	    writer.print(locale.toString());

-	}

-	writer.println();

-	Enumeration names = request.getParameterNames();

-	while (names.hasMoreElements()) {

-	    String name = (String) names.nextElement();

-	    writer.print("         parameter=" + name + "=");

-	    String values[] = request.getParameterValues(name);

-	    for (int i = 0; i < values.length; i++) {

-	        if (i > 0)

-		    writer.print(", ");

-		writer.print(values[i]);

-	    }

-	    writer.println();

-	}

-	writer.println("          protocol=" + request.getProtocol());

-	writer.println("        remoteAddr=" + request.getRemoteAddr());

-	writer.println("        remoteHost=" + request.getRemoteHost());

-	writer.println("            scheme=" + request.getScheme());

-	writer.println("        serverName=" + request.getServerName());

-	writer.println("        serverPort=" + request.getServerPort());

-	writer.println("          isSecure=" + request.isSecure());

-

-	// Render the HTTP servlet request properties

-	if (request instanceof HttpServletRequest) {

-	    writer.println("---------------------------------------------");

-	    HttpServletRequest hrequest = (HttpServletRequest) request;

-	    writer.println("       contextPath=" + hrequest.getContextPath());

-	    Cookie cookies[] = hrequest.getCookies();

-            if (cookies == null)

-                cookies = new Cookie[0];

-	    for (int i = 0; i < cookies.length; i++) {

-	        writer.println("            cookie=" + cookies[i].getName() +

-			       "=" + cookies[i].getValue());

-	    }

-	    names = hrequest.getHeaderNames();

-	    while (names.hasMoreElements()) {

-	        String name = (String) names.nextElement();

-		String value = hrequest.getHeader(name);

-	        writer.println("            header=" + name + "=" + value);

-	    }

-	    writer.println("            method=" + hrequest.getMethod());

-	    writer.println("          pathInfo=" + hrequest.getPathInfo());

-	    writer.println("       queryString=" + hrequest.getQueryString());

-	    writer.println("        remoteUser=" + hrequest.getRemoteUser());

-	    writer.println("requestedSessionId=" +

-			   hrequest.getRequestedSessionId());

-	    writer.println("        requestURI=" + hrequest.getRequestURI());

-	    writer.println("       servletPath=" + hrequest.getServletPath());

-	}

-	writer.println("=============================================");

-

-	// Log the resulting string

-	writer.flush();

-	filterConfig.getServletContext().log(sw.getBuffer().toString());

-

-	// Pass control on to the next filter

-        chain.doFilter(request, response);

-

-    }

-

-

-    /**

-     * Place this filter into service.

-     *

-     * @param filterConfig The filter configuration object

-     */

-    public void init(FilterConfig filterConfig) throws ServletException {

-

-	this.filterConfig = filterConfig;

-

-    }

-

-

-    /**

-     * Return a String representation of this object.

-     */

-    public String toString() {

-

-	if (filterConfig == null)

-	    return ("RequestDumperFilter()");

-	StringBuffer sb = new StringBuffer("RequestDumperFilter(");

-	sb.append(filterConfig);

-	sb.append(")");

-	return (sb.toString());

-

-    }

-

-

-}

-

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/filters/SetCharacterEncodingFilter.class b/tomcat-uid/webapps/examples/WEB-INF/classes/filters/SetCharacterEncodingFilter.class
deleted file mode 100644
index 94be00b..0000000
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/filters/SetCharacterEncodingFilter.class
+++ /dev/null
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/filters/SetCharacterEncodingFilter.java b/tomcat-uid/webapps/examples/WEB-INF/classes/filters/SetCharacterEncodingFilter.java
deleted file mode 100644
index 9373222..0000000
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/filters/SetCharacterEncodingFilter.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*

-* 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 filters;

-

-

-import java.io.IOException;

-import javax.servlet.Filter;

-import javax.servlet.FilterChain;

-import javax.servlet.FilterConfig;

-import javax.servlet.ServletException;

-import javax.servlet.ServletRequest;

-import javax.servlet.ServletResponse;

-

-

-/**

- * <p>Example filter that sets the character encoding to be used in parsing the

- * incoming request, either unconditionally or only if the client did not

- * specify a character encoding.  Configuration of this filter is based on

- * the following initialization parameters:</p>

- * <ul>

- * <li><strong>encoding</strong> - The character encoding to be configured

- *     for this request, either conditionally or unconditionally based on

- *     the <code>ignore</code> initialization parameter.  This parameter

- *     is required, so there is no default.</li>

- * <li><strong>ignore</strong> - If set to "true", any character encoding

- *     specified by the client is ignored, and the value returned by the

- *     <code>selectEncoding()</code> method is set.  If set to "false,

- *     <code>selectEncoding()</code> is called <strong>only</strong> if the

- *     client has not already specified an encoding.  By default, this

- *     parameter is set to "true".</li>

- * </ul>

- *

- * <p>Although this filter can be used unchanged, it is also easy to

- * subclass it and make the <code>selectEncoding()</code> method more

- * intelligent about what encoding to choose, based on characteristics of

- * the incoming request (such as the values of the <code>Accept-Language</code>

- * and <code>User-Agent</code> headers, or a value stashed in the current

- * user's session.</p>

- *

- * @author Craig McClanahan

- */

-public class SetCharacterEncodingFilter implements Filter {

-

-

-    // ----------------------------------------------------- Instance Variables

-

-

-    /**

-     * The default character encoding to set for requests that pass through

-     * this filter.

-     */

-    protected String encoding = null;

-

-

-    /**

-     * The filter configuration object we are associated with.  If this value

-     * is null, this filter instance is not currently configured.

-     */

-    protected FilterConfig filterConfig = null;

-

-

-    /**

-     * Should a character encoding specified by the client be ignored?

-     */

-    protected boolean ignore = true;

-

-

-    // --------------------------------------------------------- Public Methods

-

-

-    /**

-     * Take this filter out of service.

-     */

-    public void destroy() {

-

-        this.encoding = null;

-        this.filterConfig = null;

-

-    }

-

-

-    /**

-     * Select and set (if specified) the character encoding to be used to

-     * interpret request parameters for this request.

-     *

-     * @param request The servlet request we are processing

-     * @param result The servlet response we are creating

-     * @param chain The filter chain we are processing

-     *

-     * @exception IOException if an input/output error occurs

-     * @exception ServletException if a servlet error occurs

-     */

-    public void doFilter(ServletRequest request, ServletResponse response,

-                         FilterChain chain)

-	throws IOException, ServletException {

-

-        // Conditionally select and set the character encoding to be used

-        if (ignore || (request.getCharacterEncoding() == null)) {

-            String encoding = selectEncoding(request);

-            if (encoding != null)

-                request.setCharacterEncoding(encoding);

-        }

-

-	// Pass control on to the next filter

-        chain.doFilter(request, response);

-

-    }

-

-

-    /**

-     * Place this filter into service.

-     *

-     * @param filterConfig The filter configuration object

-     */

-    public void init(FilterConfig filterConfig) throws ServletException {

-

-	this.filterConfig = filterConfig;

-        this.encoding = filterConfig.getInitParameter("encoding");

-        String value = filterConfig.getInitParameter("ignore");

-        if (value == null)

-            this.ignore = true;

-        else if (value.equalsIgnoreCase("true"))

-            this.ignore = true;

-        else if (value.equalsIgnoreCase("yes"))

-            this.ignore = true;

-        else

-            this.ignore = false;

-

-    }

-

-

-    // ------------------------------------------------------ Protected Methods

-

-

-    /**

-     * Select an appropriate character encoding to be used, based on the

-     * characteristics of the current request and/or filter initialization

-     * parameters.  If no character encoding should be set, return

-     * <code>null</code>.

-     * <p>

-     * The default implementation unconditionally returns the value configured

-     * by the <strong>encoding</strong> initialization parameter for this

-     * filter.

-     *

-     * @param request The servlet request we are processing

-     */

-    protected String selectEncoding(ServletRequest request) {

-

-        return (this.encoding);

-

-    }

-

-

-}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/BookBean.class b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/BookBean.class
index 98b8c31..e817362 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/BookBean.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/BookBean.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/BookBean.java b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/BookBean.java
index 5e5dec4..cc7e805 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/BookBean.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/BookBean.java
@@ -22,7 +22,7 @@
     private String title;

     private String author;

     private String isbn;

-    

+

     public BookBean( String title, String author, String isbn ) {

         this.title = title;

         this.author = author;

@@ -32,13 +32,13 @@
     public String getTitle() {

         return this.title;

     }

-    

+

     public String getAuthor() {

         return this.author;

     }

-    

+

     public String getIsbn() {

         return this.isbn;

     }

-    

+

 }

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/FooBean.class b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/FooBean.class
index 6605d19..f624400 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/FooBean.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/FooBean.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/FooBean.java b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/FooBean.java
index c7f4d39..057a581 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/FooBean.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/FooBean.java
@@ -20,17 +20,17 @@
 

 public class FooBean {

     private String bar;

-    

+

     public FooBean() {

         bar = "Initial value";

     }

-    

+

     public String getBar() {

         return this.bar;

     }

-    

+

     public void setBar(String bar) {

         this.bar = bar;

     }

-    

+

 }

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/ValuesBean.class b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/ValuesBean.class
new file mode 100644
index 0000000..1ae6315
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/ValuesBean.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/ValuesBean.java b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/ValuesBean.java
new file mode 100644
index 0000000..b7ff056
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/ValuesBean.java
@@ -0,0 +1,52 @@
+/*

+* 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 jsp2.examples;

+

+/**

+ * Accept and display a value.

+ */

+public class ValuesBean {

+    private String string;

+    private double doubleValue;

+    private long longValue;

+

+    public String getStringValue() {

+        return this.string;

+    }

+

+    public void setStringValue(String string) {

+        this.string = string;

+    }

+

+    public double getDoubleValue() {

+        return doubleValue;

+    }

+

+    public void setDoubleValue(double doubleValue) {

+        this.doubleValue = doubleValue;

+    }

+

+    public long getLongValue() {

+        return longValue;

+    }

+

+    public void setLongValue(long longValue) {

+        this.longValue = longValue;

+    }

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/el/Functions.class b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/el/Functions.class
index e0a869c..54f11ef 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/el/Functions.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/el/Functions.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/el/Functions.java b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/el/Functions.java
index c42c0f7..cc15bf3 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/el/Functions.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/el/Functions.java
@@ -16,28 +16,30 @@
 */

 package jsp2.examples.el;

 

+import java.util.Locale;

+

 /**

  * Defines the functions for the jsp2 example tag library.

- * 

+ *

  * <p>Each function is defined as a static method.</p>

  */

 public class Functions {

     public static String reverse( String text ) {

-        return new StringBuffer( text ).reverse().toString();

+        return new StringBuilder( text ).reverse().toString();

     }

 

     public static int numVowels( String text ) {

         String vowels = "aeiouAEIOU";

-	int result = 0;

+        int result = 0;

         for( int i = 0; i < text.length(); i++ ) {

-	    if( vowels.indexOf( text.charAt( i ) ) != -1 ) {

-	        result++;

-	    }

-	}

-	return result;

+            if( vowels.indexOf( text.charAt( i ) ) != -1 ) {

+                result++;

+            }

+        }

+        return result;

     }

 

     public static String caps( String text ) {

-        return text.toUpperCase();

+        return text.toUpperCase(Locale.ENGLISH);

     }

 }

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/EchoAttributesTag.class b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/EchoAttributesTag.class
index 5cf67e4..f1ca8cd 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/EchoAttributesTag.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/EchoAttributesTag.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/EchoAttributesTag.java b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/EchoAttributesTag.java
index c1a5f92..4dd5322 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/EchoAttributesTag.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/EchoAttributesTag.java
@@ -18,37 +18,40 @@
 

 package jsp2.examples.simpletag;

 

+import java.io.IOException;

+import java.util.ArrayList;

+

 import javax.servlet.jsp.JspException;

 import javax.servlet.jsp.JspWriter;

-import javax.servlet.jsp.tagext.SimpleTagSupport;

 import javax.servlet.jsp.tagext.DynamicAttributes;

-import java.util.ArrayList;

-import java.io.IOException;

+import javax.servlet.jsp.tagext.SimpleTagSupport;

 

 /**

- * SimpleTag handler that echoes all its attributes 

+ * SimpleTag handler that echoes all its attributes

  */

-public class EchoAttributesTag 

+public class EchoAttributesTag

     extends SimpleTagSupport

     implements DynamicAttributes

 {

-    private ArrayList keys = new ArrayList();

-    private ArrayList values = new ArrayList();

+    private ArrayList<String> keys = new ArrayList<String>();

+    private ArrayList<Object> values = new ArrayList<Object>();

 

+    @Override

     public void doTag() throws JspException, IOException {

-	JspWriter out = getJspContext().getOut();

-	for( int i = 0; i < keys.size(); i++ ) {

-	    String key = (String)keys.get( i );

-	    Object value = values.get( i );

-	    out.println( "<li>" + key + " = " + value + "</li>" );

+        JspWriter out = getJspContext().getOut();

+        for( int i = 0; i < keys.size(); i++ ) {

+            String key = keys.get( i );

+            Object value = values.get( i );

+            out.println( "<li>" + key + " = " + value + "</li>" );

         }

     }

 

-    public void setDynamicAttribute( String uri, String localName, 

-	Object value ) 

-	throws JspException

+    @Override

+    public void setDynamicAttribute( String uri, String localName,

+        Object value )

+        throws JspException

     {

-	keys.add( localName );

-	values.add( value );

+        keys.add( localName );

+        values.add( value );

     }

 }

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/FindBookSimpleTag.class b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/FindBookSimpleTag.class
index dd95ddb..4b3a691 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/FindBookSimpleTag.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/FindBookSimpleTag.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/FindBookSimpleTag.java b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/FindBookSimpleTag.java
index 3d4357d..7554558 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/FindBookSimpleTag.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/FindBookSimpleTag.java
@@ -20,6 +20,7 @@
 

 import javax.servlet.jsp.JspException;

 import javax.servlet.jsp.tagext.SimpleTagSupport;

+

 import jsp2.examples.BookBean;

 

 /**

@@ -28,17 +29,18 @@
  */

 public class FindBookSimpleTag extends SimpleTagSupport {

     private String var;

-    

+

     private static final String BOOK_TITLE = "The Lord of the Rings";

     private static final String BOOK_AUTHOR = "J. R. R. Tolkein";

     private static final String BOOK_ISBN = "0618002251";

 

+    @Override

     public void doTag() throws JspException {

         BookBean book = new BookBean( BOOK_TITLE, BOOK_AUTHOR, BOOK_ISBN );

         getJspContext().setAttribute( this.var, book );

     }

 

     public void setVar( String var ) {

-	this.var = var;

+        this.var = var;

     }

 }

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/HelloWorldSimpleTag.class b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/HelloWorldSimpleTag.class
index ff44a1d..21450f5 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/HelloWorldSimpleTag.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/HelloWorldSimpleTag.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/HelloWorldSimpleTag.java b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/HelloWorldSimpleTag.java
index 3085739..e068b65 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/HelloWorldSimpleTag.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/HelloWorldSimpleTag.java
@@ -18,15 +18,17 @@
 

 package jsp2.examples.simpletag;

 

+import java.io.IOException;

+

 import javax.servlet.jsp.JspException;

 import javax.servlet.jsp.tagext.SimpleTagSupport;

-import java.io.IOException;

 

 /**

  * SimpleTag handler that prints "Hello, world!"

  */

 public class HelloWorldSimpleTag extends SimpleTagSupport {

+    @Override

     public void doTag() throws JspException, IOException {

-	getJspContext().getOut().write( "Hello, world!" );

+        getJspContext().getOut().write( "Hello, world!" );

     }

 }

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/RepeatSimpleTag.class b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/RepeatSimpleTag.class
index 55e4835..6307468 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/RepeatSimpleTag.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/RepeatSimpleTag.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/RepeatSimpleTag.java b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/RepeatSimpleTag.java
index 9903a21..a9dda90 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/RepeatSimpleTag.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/RepeatSimpleTag.java
@@ -18,25 +18,27 @@
 

 package jsp2.examples.simpletag;

 

-import javax.servlet.jsp.JspException;

-import javax.servlet.jsp.tagext.SimpleTagSupport;

 import java.io.IOException;

 

+import javax.servlet.jsp.JspException;

+import javax.servlet.jsp.tagext.SimpleTagSupport;

+

 /**

- * SimpleTag handler that accepts a num attribute and 

+ * SimpleTag handler that accepts a num attribute and

  * invokes its body 'num' times.

  */

 public class RepeatSimpleTag extends SimpleTagSupport {

     private int num;

 

+    @Override

     public void doTag() throws JspException, IOException {

         for (int i=0; i<num; i++) {

             getJspContext().setAttribute("count", String.valueOf( i + 1 ) );

-	    getJspBody().invoke(null);

+            getJspBody().invoke(null);

         }

     }

 

     public void setNum(int num) {

-	this.num = num;

+        this.num = num;

     }

 }

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/ShuffleSimpleTag.class b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/ShuffleSimpleTag.class
index 759c5ac..3ba23ed 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/ShuffleSimpleTag.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/ShuffleSimpleTag.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/ShuffleSimpleTag.java b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/ShuffleSimpleTag.java
index 64c5b72..e796af2 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/ShuffleSimpleTag.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/ShuffleSimpleTag.java
@@ -18,22 +18,28 @@
 

 package jsp2.examples.simpletag;

 

+import java.io.IOException;

+import java.util.Random;

+

 import javax.servlet.jsp.JspException;

 import javax.servlet.jsp.tagext.JspFragment;

 import javax.servlet.jsp.tagext.SimpleTagSupport;

-import java.io.IOException;

 

 /**

  * SimpleTag handler that accepts takes three attributes of type

  * JspFragment and invokes then in a random order.

  */

 public class ShuffleSimpleTag extends SimpleTagSupport {

+    // No need for this to use SecureRandom

+    private static Random random = new Random();

+

     private JspFragment fragment1;

     private JspFragment fragment2;

     private JspFragment fragment3;

 

+    @Override

     public void doTag() throws JspException, IOException {

-        switch( (int)(Math.random() * 6) ) {

+        switch(random.nextInt(6)) {

             case 0:

                 fragment1.invoke( null );

                 fragment2.invoke( null );

@@ -70,11 +76,11 @@
     public void setFragment1( JspFragment fragment1 ) {

         this.fragment1 = fragment1;

     }

-    

+

     public void setFragment2( JspFragment fragment2 ) {

         this.fragment2 = fragment2;

     }

-    

+

     public void setFragment3( JspFragment fragment3 ) {

         this.fragment3 = fragment3;

     }

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/TileSimpleTag.class b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/TileSimpleTag.class
index c33cccc..d2162fe 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/TileSimpleTag.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/TileSimpleTag.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/TileSimpleTag.java b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/TileSimpleTag.java
index 4f37e48..b95cc31 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/TileSimpleTag.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/jsp2/examples/simpletag/TileSimpleTag.java
@@ -18,9 +18,10 @@
 

 package jsp2.examples.simpletag;

 

+import java.io.IOException;

+

 import javax.servlet.jsp.JspException;

 import javax.servlet.jsp.tagext.SimpleTagSupport;

-import java.io.IOException;

 

 /**

  * Displays a tile as a single cell in a table.

@@ -29,17 +30,18 @@
     private String color;

     private String label;

 

+    @Override

     public void doTag() throws JspException, IOException {

-	getJspContext().getOut().write( 

-	    "<td width=\"32\" height=\"32\" bgcolor=\"" + this.color + 

-	    "\"><font color=\"#ffffff\"><center>" + this.label + 

+        getJspContext().getOut().write(

+                "<td width=\"32\" height=\"32\" bgcolor=\"" + this.color +

+                "\"><font color=\"#ffffff\"><center>" + this.label +

                 "</center></font></td>" );

     }

 

     public void setColor( String color ) {

         this.color = color;

     }

-    

+

     public void setLabel( String label ) {

         this.label = label;

     }

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/listeners/ContextListener.class b/tomcat-uid/webapps/examples/WEB-INF/classes/listeners/ContextListener.class
index 43f66ff..26619b0 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/listeners/ContextListener.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/listeners/ContextListener.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/listeners/ContextListener.java b/tomcat-uid/webapps/examples/WEB-INF/classes/listeners/ContextListener.java
index 4702546..7fd012f 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/listeners/ContextListener.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/listeners/ContextListener.java
@@ -14,7 +14,6 @@
 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

-

 package listeners;

 

 

@@ -54,10 +53,11 @@
      *

      * @param event The servlet context attribute event

      */

+    @Override

     public void attributeAdded(ServletContextAttributeEvent event) {

 

-	log("attributeAdded('" + event.getName() + "', '" +

-	    event.getValue() + "')");

+        log("attributeAdded('" + event.getName() + "', '" +

+                event.getValue() + "')");

 

     }

 

@@ -67,10 +67,11 @@
      *

      * @param event The servlet context attribute event

      */

+    @Override

     public void attributeRemoved(ServletContextAttributeEvent event) {

 

-	log("attributeRemoved('" + event.getName() + "', '" +

-	    event.getValue() + "')");

+        log("attributeRemoved('" + event.getName() + "', '" +

+                event.getValue() + "')");

 

     }

 

@@ -80,10 +81,11 @@
      *

      * @param event The servlet context attribute event

      */

+    @Override

     public void attributeReplaced(ServletContextAttributeEvent event) {

 

-	log("attributeReplaced('" + event.getName() + "', '" +

-	    event.getValue() + "')");

+        log("attributeReplaced('" + event.getName() + "', '" +

+                event.getValue() + "')");

 

     }

 

@@ -93,10 +95,11 @@
      *

      * @param event The servlet context event

      */

+    @Override

     public void contextDestroyed(ServletContextEvent event) {

 

-	log("contextDestroyed()");

-	this.context = null;

+        log("contextDestroyed()");

+        this.context = null;

 

     }

 

@@ -106,10 +109,11 @@
      *

      * @param event The servlet context event

      */

+    @Override

     public void contextInitialized(ServletContextEvent event) {

 

-	this.context = event.getServletContext();

-	log("contextInitialized()");

+        this.context = event.getServletContext();

+        log("contextInitialized()");

 

     }

 

@@ -124,10 +128,10 @@
      */

     private void log(String message) {

 

-	if (context != null)

-	    context.log("ContextListener: " + message);

-	else

-	    System.out.println("ContextListener: " + message);

+        if (context != null)

+            context.log("ContextListener: " + message);

+        else

+            System.out.println("ContextListener: " + message);

 

     }

 

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/listeners/SessionListener.class b/tomcat-uid/webapps/examples/WEB-INF/classes/listeners/SessionListener.class
index 8afca5e..22c53ea 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/listeners/SessionListener.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/listeners/SessionListener.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/listeners/SessionListener.java b/tomcat-uid/webapps/examples/WEB-INF/classes/listeners/SessionListener.java
index efac784..06a2aa8 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/listeners/SessionListener.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/listeners/SessionListener.java
@@ -1,23 +1,21 @@
 /*

-* 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.

-*/

-

+ * 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 listeners;

 

-

 import javax.servlet.ServletContext;

 import javax.servlet.ServletContextEvent;

 import javax.servlet.ServletContextListener;

@@ -26,135 +24,136 @@
 import javax.servlet.http.HttpSessionEvent;

 import javax.servlet.http.HttpSessionListener;

 

-

 /**

  * Example listener for context-related application events, which were

- * introduced in the 2.3 version of the Servlet API.  This listener

- * merely documents the occurrence of such events in the application log

- * associated with our servlet context.

+ * introduced in the 2.3 version of the Servlet API. This listener merely

+ * documents the occurrence of such events in the application log associated

+ * with our servlet context.

  *

  * @author Craig R. McClanahan

  */

-public final class SessionListener

-    implements ServletContextListener,

-	       HttpSessionAttributeListener, HttpSessionListener {

-

+public final class SessionListener implements ServletContextListener,

+        HttpSessionAttributeListener, HttpSessionListener {

 

     // ----------------------------------------------------- Instance Variables

 

-

     /**

      * The servlet context with which we are associated.

      */

     private ServletContext context = null;

 

-

     // --------------------------------------------------------- Public Methods

 

-

     /**

      * Record the fact that a servlet context attribute was added.

      *

-     * @param event The session attribute event

+     * @param event

+     *            The session attribute event

      */

+    @Override

     public void attributeAdded(HttpSessionBindingEvent event) {

 

-	log("attributeAdded('" + event.getSession().getId() + "', '" +

-	    event.getName() + "', '" + event.getValue() + "')");

+        log("attributeAdded('" + event.getSession().getId() + "', '"

+                + event.getName() + "', '" + event.getValue() + "')");

 

     }

 

-

     /**

      * Record the fact that a servlet context attribute was removed.

      *

-     * @param event The session attribute event

+     * @param event

+     *            The session attribute event

      */

+    @Override

     public void attributeRemoved(HttpSessionBindingEvent event) {

 

-	log("attributeRemoved('" + event.getSession().getId() + "', '" +

-	    event.getName() + "', '" + event.getValue() + "')");

+        log("attributeRemoved('" + event.getSession().getId() + "', '"

+                + event.getName() + "', '" + event.getValue() + "')");

 

     }

 

-

     /**

      * Record the fact that a servlet context attribute was replaced.

      *

-     * @param event The session attribute event

+     * @param event

+     *            The session attribute event

      */

+    @Override

     public void attributeReplaced(HttpSessionBindingEvent event) {

 

-	log("attributeReplaced('" + event.getSession().getId() + "', '" +

-	    event.getName() + "', '" + event.getValue() + "')");

+        log("attributeReplaced('" + event.getSession().getId() + "', '"

+                + event.getName() + "', '" + event.getValue() + "')");

 

     }

 

-

     /**

      * Record the fact that this web application has been destroyed.

      *

-     * @param event The servlet context event

+     * @param event

+     *            The servlet context event

      */

+    @Override

     public void contextDestroyed(ServletContextEvent event) {

 

-	log("contextDestroyed()");

-	this.context = null;

+        log("contextDestroyed()");

+        this.context = null;

 

     }

 

-

     /**

      * Record the fact that this web application has been initialized.

      *

-     * @param event The servlet context event

+     * @param event

+     *            The servlet context event

      */

+    @Override

     public void contextInitialized(ServletContextEvent event) {

 

-	this.context = event.getServletContext();

-	log("contextInitialized()");

+        this.context = event.getServletContext();

+        log("contextInitialized()");

 

     }

 

-

     /**

      * Record the fact that a session has been created.

      *

-     * @param event The session event

+     * @param event

+     *            The session event

      */

+    @Override

     public void sessionCreated(HttpSessionEvent event) {

 

-	log("sessionCreated('" + event.getSession().getId() + "')");

+        log("sessionCreated('" + event.getSession().getId() + "')");

 

     }

 

-

     /**

      * Record the fact that a session has been destroyed.

      *

-     * @param event The session event

+     * @param event

+     *            The session event

      */

+    @Override

     public void sessionDestroyed(HttpSessionEvent event) {

 

-	log("sessionDestroyed('" + event.getSession().getId() + "')");

+        log("sessionDestroyed('" + event.getSession().getId() + "')");

 

     }

 

-

     // -------------------------------------------------------- Private Methods

 

-

     /**

      * Log a message to the servlet context application log.

      *

-     * @param message Message to be logged

+     * @param message

+     *            Message to be logged

      */

     private void log(String message) {

 

-	if (context != null)

-	    context.log("SessionListener: " + message);

-	else

-	    System.out.println("SessionListener: " + message);

+        if (context != null)

+            context.log("SessionListener: " + message);

+        else

+            System.out.println("SessionListener: " + message);

 

     }

 

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/num/NumberGuessBean.class b/tomcat-uid/webapps/examples/WEB-INF/classes/num/NumberGuessBean.class
index 51e5b62..e2051e9 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/num/NumberGuessBean.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/num/NumberGuessBean.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/num/NumberGuessBean.java b/tomcat-uid/webapps/examples/WEB-INF/classes/num/NumberGuessBean.java
index 7eed08e..a619edb 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/num/NumberGuessBean.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/num/NumberGuessBean.java
@@ -1,19 +1,19 @@
 /*

-* 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.

-*/

+ * 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.

+ */

 

 /*

  * Originally written by Jason Hunter, http://www.servlets.com.

@@ -21,59 +21,79 @@
 

 package num;

 

-import java.util.*;

+import java.io.Serializable;

+import java.util.Random;

 

-public class NumberGuessBean {

+public class NumberGuessBean implements Serializable {

 

-  int answer;

-  boolean success;

-  String hint;

-  int numGuesses;

+    private static final long serialVersionUID = 1L;

 

-  public NumberGuessBean() {

-    reset();

-  }

+    private int answer;

+    private String hint;

+    private int numGuesses;

+    private boolean success;

+    private Random random = new Random();

 

-  public void setGuess(String guess) {

-    numGuesses++;

-

-    int g;

-    try {

-      g = Integer.parseInt(guess);

-    }

-    catch (NumberFormatException e) {

-      g = -1;

+    public NumberGuessBean() {

+        reset();

     }

 

-    if (g == answer) {

-      success = true;

+    public int getAnswer() {

+        return answer;

     }

-    else if (g == -1) {

-      hint = "a number next time";

+

+    public void setAnswer(int answer) {

+        this.answer = answer;

     }

-    else if (g < answer) {

-      hint = "higher";

+

+    public String getHint() {

+        return "" + hint;

     }

-    else if (g > answer) {

-      hint = "lower";

+

+    public void setHint(String hint) {

+        this.hint = hint;

     }

-  }

 

-  public boolean getSuccess() {

-    return success;

-  }

+    public void setNumGuesses(int numGuesses) {

+        this.numGuesses = numGuesses;

+    }

 

-  public String getHint() {

-    return "" + hint;

-  }

+    public int getNumGuesses() {

+        return numGuesses;

+    }

 

-  public int getNumGuesses() {

-    return numGuesses;

-  }

+    public boolean getSuccess() {

+        return success;

+    }

 

-  public void reset() {

-    answer = Math.abs(new Random().nextInt() % 100) + 1;

-    success = false;

-    numGuesses = 0;

-  }

+    public void setSuccess(boolean success) {

+        this.success = success;

+    }

+

+    public void setGuess(String guess) {

+        numGuesses++;

+

+        int g;

+        try {

+            g = Integer.parseInt(guess);

+        } catch (NumberFormatException e) {

+            g = -1;

+        }

+

+        if (g == answer) {

+            success = true;

+        } else if (g == -1) {

+            hint = "a number next time";

+        } else if (g < answer) {

+            hint = "higher";

+        } else if (g > answer) {

+            hint = "lower";

+        }

+    }

+

+    public void reset() {

+        answer = Math.abs(random.nextInt() % 100) + 1;

+        success = false;

+        numGuesses = 0;

+    }

 }

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/servletToJsp.class b/tomcat-uid/webapps/examples/WEB-INF/classes/servletToJsp.class
index c95143b..4432239 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/servletToJsp.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/servletToJsp.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/servletToJsp.java b/tomcat-uid/webapps/examples/WEB-INF/classes/servletToJsp.java
index cfd198e..f5cd80a 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/servletToJsp.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/servletToJsp.java
@@ -14,19 +14,26 @@
 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

-import javax.servlet.http.*;

 

-public class servletToJsp extends HttpServlet {

+import javax.servlet.http.HttpServlet;

+import javax.servlet.http.HttpServletRequest;

+import javax.servlet.http.HttpServletResponse;

 

+public class ServletToJsp extends HttpServlet {

+

+    private static final long serialVersionUID = 1L;

+

+    @Override

     public void doGet (HttpServletRequest request,

-		       HttpServletResponse response) {

+            HttpServletResponse response) {

 

-	try {

-	    // Set the attribute and Forward to hello.jsp

-	    request.setAttribute ("servletName", "servletToJsp");

-	    getServletConfig().getServletContext().getRequestDispatcher("/jsp/jsptoserv/hello.jsp").forward(request, response);

-	} catch (Exception ex) {

-	    ex.printStackTrace ();

-	}

+       try {

+           // Set the attribute and Forward to hello.jsp

+           request.setAttribute ("servletName", "servletToJsp");

+           getServletConfig().getServletContext().getRequestDispatcher(

+                   "/jsp/jsptoserv/hello.jsp").forward(request, response);

+       } catch (Exception ex) {

+           ex.printStackTrace ();

+       }

     }

 }

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/sessions/DummyCart.class b/tomcat-uid/webapps/examples/WEB-INF/classes/sessions/DummyCart.class
index 5518952..2ad60c0 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/sessions/DummyCart.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/sessions/DummyCart.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/sessions/DummyCart.java b/tomcat-uid/webapps/examples/WEB-INF/classes/sessions/DummyCart.java
index f0597e7..01480a0 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/sessions/DummyCart.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/sessions/DummyCart.java
@@ -22,7 +22,7 @@
     Vector<String> v = new Vector<String>();

     String submit = null;

     String item = null;

-    

+

     private void addItem(String name) {

         v.addElement(name);

     }

@@ -34,7 +34,7 @@
     public void setItem(String name) {

         item = name;

     }

-    

+

     public void setSubmit(String s) {

         submit = s;

     }

@@ -44,15 +44,15 @@
         v.copyInto(s);

         return s;

     }

-    

+

     public void processRequest() {

-        // null value for submit - user hit enter instead of clicking on 

+        // null value for submit - user hit enter instead of clicking on

         // "add" or "remove"

         if (submit == null || submit.equals("add"))

             addItem(item);

-        else if (submit.equals("remove")) 

+        else if (submit.equals("remove"))

             removeItem(item);

-        

+

         // reset at the end of the request

         reset();

     }

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/util/CookieFilter.class b/tomcat-uid/webapps/examples/WEB-INF/classes/util/CookieFilter.class
new file mode 100644
index 0000000..429bbac
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/util/CookieFilter.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/util/CookieFilter.java b/tomcat-uid/webapps/examples/WEB-INF/classes/util/CookieFilter.java
new file mode 100644
index 0000000..005222f
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/util/CookieFilter.java
@@ -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 util;

+

+import java.util.Locale;

+import java.util.StringTokenizer;

+

+/**

+ * Processes a cookie header and attempts to obfuscate any cookie values that

+ * represent session IDs from other web applications. Since session cookie names

+ * are configurable, as are session ID lengths, this filter is not expected to

+ * be 100% effective.

+ *

+ * It is required that the examples web application is removed in security

+ * conscious environments as documented in the Security How-To. This filter is

+ * intended to reduce the impact of failing to follow that advice. A failure by

+ * this filter to obfuscate a session ID or similar value is not a security

+ * vulnerability. In such instances the vulnerability is the failure to remove

+ * the examples web application.

+ */

+public class CookieFilter {

+

+    private static final String OBFUSCATED = "[obfuscated]";

+

+    private CookieFilter() {

+        // Hide default constructor

+    }

+

+    public static String filter(String input, String sessionId) {

+

+        StringBuilder sb = new StringBuilder(input.length());

+

+        // Cookie name value pairs are ';' separated.

+        // Session IDs don't use ; in the value so don't worry about quoted

+        // values that contain ;

+        StringTokenizer st = new StringTokenizer(input, ";");

+

+        boolean first = true;

+        while (st.hasMoreTokens()) {

+            if (first) {

+                first = false;

+            } else {

+                sb.append(';');

+            }

+            sb.append(filterNameValuePair(st.nextToken(), sessionId));

+        }

+

+

+        return sb.toString();

+    }

+

+    private static String filterNameValuePair(String input, String sessionId) {

+        int i = input.indexOf('=');

+        if (i == -1) {

+            return input;

+        }

+        String name = input.substring(0, i);

+        String value = input.substring(i + 1, input.length());

+

+        if (name.toLowerCase(Locale.ENGLISH).contains("jsessionid") &&

+                (sessionId == null || !value.contains(sessionId))) {

+            value = OBFUSCATED;

+        }

+

+        return name + "=" + value;

+    }

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/util/HTMLFilter.class b/tomcat-uid/webapps/examples/WEB-INF/classes/util/HTMLFilter.class
index 7555aa0..1ed1273 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/util/HTMLFilter.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/util/HTMLFilter.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/util/HTMLFilter.java b/tomcat-uid/webapps/examples/WEB-INF/classes/util/HTMLFilter.java
index 3d85e81..ec66a89 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/util/HTMLFilter.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/util/HTMLFilter.java
@@ -39,7 +39,7 @@
 

         char content[] = new char[message.length()];

         message.getChars(0, message.length(), content, 0);

-        StringBuffer result = new StringBuffer(content.length + 50);

+        StringBuilder result = new StringBuilder(content.length + 50);

         for (int i = 0; i < content.length; i++) {

             switch (content[i]) {

             case '<':

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/validators/DebugValidator.class b/tomcat-uid/webapps/examples/WEB-INF/classes/validators/DebugValidator.class
index 90b65ce..4a9fb4c 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/validators/DebugValidator.class
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/validators/DebugValidator.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/validators/DebugValidator.java b/tomcat-uid/webapps/examples/WEB-INF/classes/validators/DebugValidator.java
index 1ddffc4..d5accba 100644
--- a/tomcat-uid/webapps/examples/WEB-INF/classes/validators/DebugValidator.java
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/validators/DebugValidator.java
@@ -19,8 +19,9 @@
 package validators;

 

 

-import java.io.InputStream;

 import java.io.IOException;

+import java.io.InputStream;

+

 import javax.servlet.jsp.tagext.PageData;

 import javax.servlet.jsp.tagext.TagLibraryValidator;

 import javax.servlet.jsp.tagext.ValidationMessage;

@@ -55,6 +56,7 @@
      * @param uri The value of the URI argument in this directive

      * @param page The page data for this page

      */

+    @Override

     public ValidationMessage[] validate(String prefix, String uri,

                                         PageData page) {

 

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/ExamplesConfig.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/ExamplesConfig.class
new file mode 100644
index 0000000..188802e
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/ExamplesConfig.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/ExamplesConfig.java b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/ExamplesConfig.java
new file mode 100644
index 0000000..38ba838
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/ExamplesConfig.java
@@ -0,0 +1,66 @@
+/*

+ * 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 websocket;

+

+import java.util.HashSet;

+import java.util.Set;

+

+import javax.websocket.Endpoint;

+import javax.websocket.server.ServerApplicationConfig;

+import javax.websocket.server.ServerEndpointConfig;

+

+import websocket.drawboard.DrawboardEndpoint;

+import websocket.echo.EchoEndpoint;

+

+public class ExamplesConfig implements ServerApplicationConfig {

+

+    @Override

+    public Set<ServerEndpointConfig> getEndpointConfigs(

+            Set<Class<? extends Endpoint>> scanned) {

+

+        Set<ServerEndpointConfig> result = new HashSet<ServerEndpointConfig>();

+

+        if (scanned.contains(EchoEndpoint.class)) {

+            result.add(ServerEndpointConfig.Builder.create(

+                    EchoEndpoint.class,

+                    "/websocket/echoProgrammatic").build());

+        }

+

+        if (scanned.contains(DrawboardEndpoint.class)) {

+            result.add(ServerEndpointConfig.Builder.create(

+                    DrawboardEndpoint.class,

+                    "/websocket/drawboard").build());

+        }

+

+        return result;

+    }

+

+

+    @Override

+    public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scanned) {

+        // Deploy all WebSocket endpoints defined by annotations in the examples

+        // web application. Filter out all others to avoid issues when running

+        // tests on Gump

+        Set<Class<?>> results = new HashSet<Class<?>>();

+        for (Class<?> clazz : scanned) {

+            if (clazz.getPackage().getName().startsWith("websocket.")) {

+                results.add(clazz);

+            }

+        }

+        return results;

+    }

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/chat/ChatAnnotation.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/chat/ChatAnnotation.class
new file mode 100644
index 0000000..154bd54
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/chat/ChatAnnotation.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/chat/ChatAnnotation.java b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/chat/ChatAnnotation.java
new file mode 100644
index 0000000..d585d98
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/chat/ChatAnnotation.java
@@ -0,0 +1,109 @@
+/*

+ *  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 websocket.chat;

+

+import java.io.IOException;

+import java.util.Set;

+import java.util.concurrent.CopyOnWriteArraySet;

+import java.util.concurrent.atomic.AtomicInteger;

+

+import javax.websocket.OnClose;

+import javax.websocket.OnError;

+import javax.websocket.OnMessage;

+import javax.websocket.OnOpen;

+import javax.websocket.Session;

+import javax.websocket.server.ServerEndpoint;

+

+import org.apache.juli.logging.Log;

+import org.apache.juli.logging.LogFactory;

+

+import util.HTMLFilter;

+

+@ServerEndpoint(value = "/websocket/chat")

+public class ChatAnnotation {

+

+    private static final Log log = LogFactory.getLog(ChatAnnotation.class);

+

+    private static final String GUEST_PREFIX = "Guest";

+    private static final AtomicInteger connectionIds = new AtomicInteger(0);

+    private static final Set<ChatAnnotation> connections =

+            new CopyOnWriteArraySet<ChatAnnotation>();

+

+    private final String nickname;

+    private Session session;

+

+    public ChatAnnotation() {

+        nickname = GUEST_PREFIX + connectionIds.getAndIncrement();

+    }

+

+

+    @OnOpen

+    public void start(Session session) {

+        this.session = session;

+        connections.add(this);

+        String message = String.format("* %s %s", nickname, "has joined.");

+        broadcast(message);

+    }

+

+

+    @OnClose

+    public void end() {

+        connections.remove(this);

+        String message = String.format("* %s %s",

+                nickname, "has disconnected.");

+        broadcast(message);

+    }

+

+

+    @OnMessage

+    public void incoming(String message) {

+        // Never trust the client

+        String filteredMessage = String.format("%s: %s",

+                nickname, HTMLFilter.filter(message.toString()));

+        broadcast(filteredMessage);

+    }

+

+

+

+

+    @OnError

+    public void onError(Throwable t) throws Throwable {

+        log.error("Chat Error: " + t.toString(), t);

+    }

+

+

+    private static void broadcast(String msg) {

+        for (ChatAnnotation client : connections) {

+            try {

+                synchronized (client) {

+                    client.session.getBasicRemote().sendText(msg);

+                }

+            } catch (IOException e) {

+                log.debug("Chat Error: Failed to send message to client", e);

+                connections.remove(client);

+                try {

+                    client.session.close();

+                } catch (IOException e1) {

+                    // Ignore

+                }

+                String message = String.format("* %s %s",

+                        client.nickname, "has been disconnected.");

+                broadcast(message);

+            }

+        }

+    }

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Client$1.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Client$1.class
new file mode 100644
index 0000000..c152c6b
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Client$1.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Client.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Client.class
new file mode 100644
index 0000000..53bb52a
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Client.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Client.java b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Client.java
new file mode 100644
index 0000000..71e47c9
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Client.java
@@ -0,0 +1,233 @@
+/*

+ *  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 websocket.drawboard;

+

+import java.io.IOException;

+import java.util.LinkedList;

+

+import javax.websocket.CloseReason;

+import javax.websocket.CloseReason.CloseCodes;

+import javax.websocket.RemoteEndpoint.Async;

+import javax.websocket.SendHandler;

+import javax.websocket.SendResult;

+import javax.websocket.Session;

+

+import websocket.drawboard.wsmessages.AbstractWebsocketMessage;

+import websocket.drawboard.wsmessages.BinaryWebsocketMessage;

+import websocket.drawboard.wsmessages.CloseWebsocketMessage;

+import websocket.drawboard.wsmessages.StringWebsocketMessage;

+

+/**

+ * Represents a client with methods to send messages asynchronously.

+ */

+public class Client {

+

+    private final Session session;

+    private final Async async;

+

+    /**

+     * Contains the messages wich are buffered until the previous

+     * send operation has finished.

+     */

+    private final LinkedList<AbstractWebsocketMessage> messagesToSend =

+            new LinkedList<AbstractWebsocketMessage>();

+    /**

+     * If this client is currently sending a messages asynchronously.

+     */

+    private volatile boolean isSendingMessage = false;

+    /**

+     * If this client is closing. If <code>true</code>, new messages to

+     * send will be ignored.

+     */

+    private volatile boolean isClosing = false;

+    /**

+     * The length of all current buffered messages, to avoid iterating

+     * over a linked list.

+     */

+    private volatile long messagesToSendLength = 0;

+

+    public Client(Session session) {

+        this.session = session;

+        this.async = session.getAsyncRemote();

+    }

+

+    /**

+     * Asynchronously closes the Websocket session. This will wait until all

+     * remaining messages have been sent to the Client and then close

+     * the Websocket session.

+     */

+    public void close() {

+        sendMessage(new CloseWebsocketMessage());

+    }

+

+    /**

+     * Sends the given message asynchronously to the client.

+     * If there is already a async sending in progress, then the message

+     * will be buffered and sent when possible.<br><br>

+     *

+     * This method can be called from multiple threads.

+     * @param msg

+     */

+    public void sendMessage(AbstractWebsocketMessage msg) {

+        synchronized (messagesToSend) {

+            if (!isClosing) {

+                // Check if we have a Close message

+                if (msg instanceof CloseWebsocketMessage) {

+                    isClosing = true;

+                }

+

+                if (isSendingMessage) {

+                    // Check if the buffered messages exceed

+                    // a specific amount - in that case, disconnect the client

+                    // to prevent DoS.

+                    // In this case we check if there are >= 1000 messages

+                    // or length(of all messages) >= 1000000 bytes.

+                    if (messagesToSend.size() >= 1000

+                            || messagesToSendLength >= 1000000) {

+                        isClosing = true;

+

+                        // Discard the new message and close the session immediately.

+                        CloseReason cr = new CloseReason(

+                                CloseCodes.VIOLATED_POLICY,

+                                "Send Buffer exceeded");

+                        try {

+                            // TODO: close() may block if the remote endpoint doesn't read the data

+                            // (eventually there will be a TimeoutException). However, this method

+                            // (sendMessage) is intended to run asynchronous code and shouldn't

+                            // block. Otherwise it would temporarily stop processing of messages

+                            // from other clients.

+                            // Maybe call this method on another thread.

+                            // Note that when this method is called, the RemoteEndpoint.Async

+                            // is still in the process of sending data, so there probably should

+                            // be another way to abort the Websocket connection.

+                            // Ideally, there should be some abort() method that cancels the

+                            // connection immediately...

+                            session.close(cr);

+                        } catch (IOException e) {

+                            // Ignore

+                        }

+

+                    } else {

+

+                        // Check if the last message and the new message are

+                        // String messages - in that case we concatenate them

+                        // to reduce TCP overhead (using ";" as separator).

+                        if (msg instanceof StringWebsocketMessage

+                                && !messagesToSend.isEmpty()

+                                && messagesToSend.getLast()

+                                instanceof StringWebsocketMessage) {

+

+                            StringWebsocketMessage ms =

+                                    (StringWebsocketMessage) messagesToSend.removeLast();

+                            messagesToSendLength -= calculateMessageLength(ms);

+

+                            String concatenated = ms.getString() + ";" +

+                                    ((StringWebsocketMessage) msg).getString();

+                            msg = new StringWebsocketMessage(concatenated);

+                        }

+

+                        messagesToSend.add(msg);

+                        messagesToSendLength += calculateMessageLength(msg);

+                    }

+                } else {

+                    isSendingMessage = true;

+                    internalSendMessageAsync(msg);

+                }

+            }

+

+        }

+    }

+

+    private long calculateMessageLength(AbstractWebsocketMessage msg) {

+        if (msg instanceof BinaryWebsocketMessage) {

+            return ((BinaryWebsocketMessage) msg).getBytes().capacity();

+        } else if (msg instanceof StringWebsocketMessage) {

+            return ((StringWebsocketMessage) msg).getString().length() * 2;

+        }

+

+        return 0;

+    }

+

+    /**

+     * Internally sends the messages asynchronously.

+     * @param msg

+     */

+    private void internalSendMessageAsync(AbstractWebsocketMessage msg) {

+        try {

+            if (msg instanceof StringWebsocketMessage) {

+                StringWebsocketMessage sMsg = (StringWebsocketMessage) msg;

+                async.sendText(sMsg.getString(), sendHandler);

+

+            } else if (msg instanceof BinaryWebsocketMessage) {

+                BinaryWebsocketMessage bMsg = (BinaryWebsocketMessage) msg;

+                async.sendBinary(bMsg.getBytes(), sendHandler);

+

+            } else if (msg instanceof CloseWebsocketMessage) {

+                // Close the session.

+                session.close();

+            }

+        } catch (IllegalStateException ex) {

+            // Trying to write to the client when the session has

+            // already been closed.

+            // Ignore

+        } catch (IOException ex) {

+            // Trying to write to the client when the session has

+            // already been closed.

+            // Ignore

+        }

+    }

+

+

+

+    /**

+     * SendHandler that will continue to send buffered messages.

+     */

+    private final SendHandler sendHandler = new SendHandler() {

+        @Override

+        public void onResult(SendResult result) {

+            if (!result.isOK()) {

+                // Message could not be sent. In this case, we don't

+                // set isSendingMessage to false because we must assume the connection

+                // broke (and onClose will be called), so we don't try to send

+                // other messages.

+                // As a precaution, we close the session (e.g. if a send timeout occured).

+                // TODO: session.close() blocks, while this handler shouldn't block.

+                // Ideally, there should be some abort() method that cancels the

+                // connection immediately...

+                try {

+                    session.close();

+                } catch (IOException ex) {

+                    // Ignore

+                }

+            }

+            synchronized (messagesToSend) {

+

+                if (!messagesToSend.isEmpty()) {

+                    AbstractWebsocketMessage msg = messagesToSend.remove();

+                    messagesToSendLength -= calculateMessageLength(msg);

+

+                    internalSendMessageAsync(msg);

+

+                } else {

+                    isSendingMessage = false;

+                }

+

+            }

+        }

+    };

+

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawMessage$ParseException.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawMessage$ParseException.class
new file mode 100644
index 0000000..51943a6
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawMessage$ParseException.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawMessage.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawMessage.class
new file mode 100644
index 0000000..f37e237
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawMessage.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawMessage.java b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawMessage.java
new file mode 100644
index 0000000..c218cc8
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawMessage.java
@@ -0,0 +1,270 @@
+/*

+ *  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 websocket.drawboard;

+

+import java.awt.BasicStroke;

+import java.awt.Color;

+import java.awt.Graphics2D;

+import java.awt.geom.Arc2D;

+import java.awt.geom.Line2D;

+import java.awt.geom.Rectangle2D;

+

+/**

+ * A message that represents a drawing action.

+ * Note that we use primitive types instead of Point, Color etc.

+ * to reduce object allocation.<br><br>

+ *

+ * TODO: But a Color objects needs to be created anyway for drawing this

+ * onto a Graphics2D object, so this probably does not save much.

+ */

+public final class DrawMessage {

+

+    private int type;

+    private byte colorR, colorG, colorB, colorA;

+    private double thickness;

+    private double x1, y1, x2, y2;

+    private boolean lastInChain;

+

+    /**

+     * The type.<br>

+     * 1: Brush<br>

+     * 2: Line<br>

+     * 3: Rectangle<br>

+     * 4: Ellipse

+     */

+    public int getType() {

+        return type;

+    }

+    public void setType(int type) {

+        this.type = type;

+    }

+

+    public double getThickness() {

+        return thickness;

+    }

+    public void setThickness(double thickness) {

+        this.thickness = thickness;

+    }

+

+    public byte getColorR() {

+        return colorR;

+    }

+    public void setColorR(byte colorR) {

+        this.colorR = colorR;

+    }

+    public byte getColorG() {

+        return colorG;

+    }

+    public void setColorG(byte colorG) {

+        this.colorG = colorG;

+    }

+    public byte getColorB() {

+        return colorB;

+    }

+    public void setColorB(byte colorB) {

+        this.colorB = colorB;

+    }

+    public byte getColorA() {

+        return colorA;

+    }

+    public void setColorA(byte colorA) {

+        this.colorA = colorA;

+    }

+

+    public double getX1() {

+        return x1;

+    }

+    public void setX1(double x1) {

+        this.x1 = x1;

+    }

+    public double getX2() {

+        return x2;

+    }

+    public void setX2(double x2) {

+        this.x2 = x2;

+    }

+    public double getY1() {

+        return y1;

+    }

+    public void setY1(double y1) {

+        this.y1 = y1;

+    }

+    public double getY2() {

+        return y2;

+    }

+    public void setY2(double y2) {

+        this.y2 = y2;

+    }

+

+    /**

+     * Specifies if this DrawMessage is the last one in a chain

+     * (e.g. a chain of brush paths).<br>

+     * Currently it is unused.

+     */

+    public boolean isLastInChain() {

+        return lastInChain;

+    }

+    public void setLastInChain(boolean lastInChain) {

+        this.lastInChain = lastInChain;

+    }

+

+

+

+    public DrawMessage(int type, byte colorR, byte colorG, byte colorB,

+            byte colorA, double thickness, double x1, double x2, double y1,

+            double y2, boolean lastInChain) {

+

+        this.type = type;

+        this.colorR = colorR;

+        this.colorG = colorG;

+        this.colorB = colorB;

+        this.colorA = colorA;

+        this.thickness = thickness;

+        this.x1 = x1;

+        this.x2 = x2;

+        this.y1 = y1;

+        this.y2 = y2;

+        this.lastInChain = lastInChain;

+    }

+

+

+    /**

+     * Draws this DrawMessage onto the given Graphics2D.

+     * @param g

+     */

+    public void draw(Graphics2D g) {

+

+        g.setStroke(new BasicStroke((float) thickness,

+                BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER));

+        g.setColor(new Color(colorR & 0xFF, colorG & 0xFF, colorB & 0xFF,

+                colorA & 0xFF));

+

+        if (x1 == x2 && y1 == y2) {

+            // Always draw as arc to meet the behavior in the HTML5 Canvas.

+            Arc2D arc = new Arc2D.Double(x1, y1, 0, 0,

+                    0d, 360d, Arc2D.OPEN);

+            g.draw(arc);

+

+        } else if (type == 1 || type == 2) {

+            // Draw a line.

+            Line2D line = new Line2D.Double(x1, y1, x2, y2);

+            g.draw(line);

+

+        } else if (type == 3 || type == 4) {

+            double x1 = this.x1, x2 = this.x2,

+                    y1 = this.y1, y2 = this.y2;

+            if (x1 > x2) {

+                x1 = this.x2;

+                x2 = this.x1;

+            }

+            if (y1 > y2) {

+                y1 = this.y2;

+                y2 = this.y1;

+            }

+

+            // TODO: If (x1 == x2 || y1 == y2) draw as line.

+

+            if (type == 3) {

+                // Draw a rectangle.

+                Rectangle2D rect = new Rectangle2D.Double(x1, y1,

+                        x2 - x1, y2 - y1);

+                g.draw(rect);

+

+            } else if (type == 4) {

+                // Draw an ellipse.

+                Arc2D arc = new Arc2D.Double(x1, y1, x2 - x1, y2 - y1,

+                        0d, 360d, Arc2D.OPEN);

+                g.draw(arc);

+

+            }

+        }

+    }

+

+    /**

+     * Converts this message into a String representation that

+     * can be sent over WebSocket.<br>

+     * Since a DrawMessage consists only of numbers,

+     * we concatenate those numbers with a ",".

+     */

+    @Override

+    public String toString() {

+

+        return type + "," + (colorR & 0xFF) + "," + (colorG & 0xFF) + ","

+                + (colorB & 0xFF) + "," + (colorA & 0xFF) + "," + thickness

+                + "," + x1 + "," + y1 + "," + x2 + "," + y2 + ","

+                + (lastInChain ? "1" : "0");

+    }

+

+    public static DrawMessage parseFromString(String str)

+            throws ParseException {

+

+        int type;

+        byte[] colors = new byte[4];

+        double thickness;

+        double[] coords = new double[4];

+        boolean last;

+

+        try {

+            String[] elements = str.split(",");

+

+            type = Integer.parseInt(elements[0]);

+            if (!(type >= 1 && type <= 4))

+                throw new ParseException("Invalid type: " + type);

+

+            for (int i = 0; i < colors.length; i++) {

+                colors[i] = (byte) Integer.parseInt(elements[1 + i]);

+            }

+

+            thickness = Double.parseDouble(elements[5]);

+            if (Double.isNaN(thickness) || thickness < 0 || thickness > 100)

+                throw new ParseException("Invalid thickness: " + thickness);

+

+            for (int i = 0; i < coords.length; i++) {

+                coords[i] = Double.parseDouble(elements[6 + i]);

+                if (Double.isNaN(coords[i]))

+                    throw new ParseException("Invalid coordinate: "

+                            + coords[i]);

+            }

+

+            last = !"0".equals(elements[10]);

+

+        } catch (RuntimeException ex) {

+            throw new ParseException(ex);

+        }

+

+        DrawMessage m = new DrawMessage(type, colors[0], colors[1],

+                colors[2], colors[3], thickness, coords[0], coords[2],

+                coords[1], coords[3], last);

+

+        return m;

+    }

+

+    public static class ParseException extends Exception {

+        private static final long serialVersionUID = -6651972769789842960L;

+

+        public ParseException(Throwable root) {

+            super(root);

+        }

+

+        public ParseException(String message) {

+            super(message);

+        }

+    }

+

+

+

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardContextListener.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardContextListener.class
new file mode 100644
index 0000000..f6724e6
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardContextListener.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardContextListener.java b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardContextListener.java
new file mode 100644
index 0000000..4ba2b38
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardContextListener.java
@@ -0,0 +1,37 @@
+/*

+ *  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 websocket.drawboard;

+

+import javax.servlet.ServletContextEvent;

+import javax.servlet.ServletContextListener;

+

+public final class DrawboardContextListener implements ServletContextListener {

+

+    @Override

+    public void contextInitialized(ServletContextEvent sce) {

+        // NO-OP

+    }

+

+    @Override

+    public void contextDestroyed(ServletContextEvent sce) {

+        // Shutdown our room.

+        Room room = DrawboardEndpoint.getRoom(false);

+        if (room != null) {

+            room.shutdown();

+        }

+    }

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardEndpoint$1.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardEndpoint$1.class
new file mode 100644
index 0000000..3add6a1
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardEndpoint$1.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardEndpoint$2.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardEndpoint$2.class
new file mode 100644
index 0000000..ee2f923
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardEndpoint$2.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardEndpoint$3$1.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardEndpoint$3$1.class
new file mode 100644
index 0000000..86ff72e
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardEndpoint$3$1.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardEndpoint$3.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardEndpoint$3.class
new file mode 100644
index 0000000..9a9499e
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardEndpoint$3.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardEndpoint.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardEndpoint.class
new file mode 100644
index 0000000..5f35890
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardEndpoint.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardEndpoint.java b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardEndpoint.java
new file mode 100644
index 0000000..75d86f3
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/DrawboardEndpoint.java
@@ -0,0 +1,236 @@
+/*

+ *  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 websocket.drawboard;

+

+import java.io.EOFException;

+import java.io.IOException;

+

+import javax.websocket.CloseReason;

+import javax.websocket.Endpoint;

+import javax.websocket.EndpointConfig;

+import javax.websocket.MessageHandler;

+import javax.websocket.Session;

+

+import org.apache.juli.logging.Log;

+import org.apache.juli.logging.LogFactory;

+

+import websocket.drawboard.DrawMessage.ParseException;

+import websocket.drawboard.wsmessages.StringWebsocketMessage;

+

+

+public final class DrawboardEndpoint extends Endpoint {

+

+    private static final Log log =

+            LogFactory.getLog(DrawboardEndpoint.class);

+

+

+    /**

+     * Our room where players can join.

+     */

+    private static volatile Room room = null;

+    private static final Object roomLock = new Object();

+

+    public static Room getRoom(boolean create) {

+        if (create) {

+            if (room == null) {

+                synchronized (roomLock) {

+                    if (room == null) {

+                        room = new Room();

+                    }

+                }

+            }

+            return room;

+        } else {

+            return room;

+        }

+    }

+

+    /**

+     * The player that is associated with this Endpoint and the current room.

+     * Note that this variable is only accessed from the Room Thread.<br><br>

+     *

+     * TODO: Currently, Tomcat uses an Endpoint instance once - however

+     * the java doc of endpoint says:

+     * "Each instance of a websocket endpoint is guaranteed not to be called by

+     * more than one thread at a time per active connection."

+     * This could mean that after calling onClose(), the instance

+     * could be reused for another connection so onOpen() will get called

+     * (possibly from another thread).<br>

+     * If this is the case, we would need a variable holder for the variables

+     * that are accessed by the Room thread, and read the reference to the holder

+     * at the beginning of onOpen, onMessage, onClose methods to ensure the room

+     * thread always gets the correct instance of the variable holder.

+     */

+    private Room.Player player;

+

+

+    @Override

+    public void onOpen(Session session, EndpointConfig config) {

+        // Set maximum messages size to 10.000 bytes.

+        session.setMaxTextMessageBufferSize(10000);

+        session.addMessageHandler(stringHandler);

+        final Client client = new Client(session);

+

+        final Room room = getRoom(true);

+        room.invokeAndWait(new Runnable() {

+            @Override

+            public void run() {

+                try {

+

+                    // Create a new Player and add it to the room.

+                    try {

+                        player = room.createAndAddPlayer(client);

+                    } catch (IllegalStateException ex) {

+                        // Probably the max. number of players has been

+                        // reached.

+                        client.sendMessage(new StringWebsocketMessage(

+                                "0" + ex.getLocalizedMessage()));

+                        // Close the connection.

+                        client.close();

+                    }

+

+                } catch (RuntimeException ex) {

+                    log.error("Unexpected exception: " + ex.toString(), ex);

+                }

+            }

+        });

+

+    }

+

+

+    @Override

+    public void onClose(Session session, CloseReason closeReason) {

+        Room room = getRoom(false);

+        if (room != null) {

+            room.invokeAndWait(new Runnable() {

+                @Override

+                public void run() {

+                    try {

+                        // Player can be null if it couldn't enter the room

+                        if (player != null) {

+                            // Remove this player from the room.

+                            player.removeFromRoom();

+

+                            // Set player to null to prevent NPEs when onMessage events

+                            // are processed (from other threads) after onClose has been

+                            // called from different thread which closed the Websocket session.

+                            player = null;

+                        }

+                    } catch (RuntimeException ex) {

+                        log.error("Unexpected exception: " + ex.toString(), ex);

+                    }

+                }

+            });

+        }

+    }

+

+

+

+    @Override

+    public void onError(Session session, Throwable t) {

+        // Most likely cause is a user closing their browser. Check to see if

+        // the root cause is EOF and if it is ignore it.

+        // Protect against infinite loops.

+        int count = 0;

+        Throwable root = t;

+        while (root.getCause() != null && count < 20) {

+            root = root.getCause();

+            count ++;

+        }

+        if (root instanceof EOFException) {

+            // Assume this is triggered by the user closing their browser and

+            // ignore it.

+        } else if (!session.isOpen() && root instanceof IOException) {

+            // IOException after close. Assume this is a variation of the user

+            // closing their browser (or refreshing very quickly) and ignore it.

+        } else {

+            log.error("onError: " + t.toString(), t);

+        }

+    }

+

+

+

+    private final MessageHandler.Whole<String> stringHandler =

+            new MessageHandler.Whole<String>() {

+

+        @Override

+        public void onMessage(final String message) {

+            // Invoke handling of the message in the room.

+            room.invokeAndWait(new Runnable() {

+                @Override

+                public void run() {

+                    try {

+

+                        // Currently, the only types of messages the client will send

+                        // are draw messages prefixed by a Message ID

+                        // (starting with char '1'), and pong messages (starting

+                        // with char '0').

+                        // Draw messages should look like this:

+                        // ID|type,colR,colB,colG,colA,thickness,x1,y1,x2,y2,lastInChain

+

+                        boolean dontSwallowException = false;

+                        try {

+                            char messageType = message.charAt(0);

+                            String messageContent = message.substring(1);

+                            switch (messageType) {

+                            case '0':

+                                // Pong message.

+                                // Do nothing.

+                                break;

+

+                            case '1':

+                                // Draw message

+                                int indexOfChar = messageContent.indexOf('|');

+                                long msgId = Long.parseLong(

+                                        messageContent.substring(0, indexOfChar));

+

+                                DrawMessage msg = DrawMessage.parseFromString(

+                                        messageContent.substring(indexOfChar + 1));

+

+                                // Don't ignore RuntimeExceptions thrown by

+                                // this method

+                                // TODO: Find a better solution than this variable

+                                dontSwallowException = true;

+                                if (player != null) {

+                                    player.handleDrawMessage(msg, msgId);

+                                }

+                                dontSwallowException = false;

+

+                                break;

+                            }

+

+                        } catch (RuntimeException ex) {

+                            // Client sent invalid data.

+                            // Ignore, TODO: maybe close connection

+                            if (dontSwallowException) {

+                                throw ex;

+                            }

+                        } catch (ParseException ex) {

+                            // Client sent invalid data.

+                            // Ignore, TODO: maybe close connection

+                        }

+                    } catch (RuntimeException ex) {

+                        log.error("Unexpected exception: " + ex.toString(), ex);

+                    }

+                }

+            });

+

+        }

+    };

+

+

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Room$1$1.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Room$1$1.class
new file mode 100644
index 0000000..604acea
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Room$1$1.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Room$1.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Room$1.class
new file mode 100644
index 0000000..ba034ac
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Room$1.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Room$2.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Room$2.class
new file mode 100644
index 0000000..6ef92d7
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Room$2.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Room$MessageType.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Room$MessageType.class
new file mode 100644
index 0000000..1e5b6bc
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Room$MessageType.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Room$Player.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Room$Player.class
new file mode 100644
index 0000000..bf50834
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Room$Player.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Room.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Room.class
new file mode 100644
index 0000000..7c9e605
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Room.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Room.java b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Room.java
new file mode 100644
index 0000000..9e3b361
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/Room.java
@@ -0,0 +1,490 @@
+/*

+ *  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 websocket.drawboard;

+

+import java.awt.Color;

+import java.awt.Graphics2D;

+import java.awt.RenderingHints;

+import java.awt.image.BufferedImage;

+import java.io.ByteArrayOutputStream;

+import java.io.IOException;

+import java.nio.ByteBuffer;

+import java.util.ArrayList;

+import java.util.List;

+import java.util.Timer;

+import java.util.TimerTask;

+import java.util.concurrent.locks.ReentrantLock;

+

+import javax.imageio.ImageIO;

+

+import websocket.drawboard.wsmessages.BinaryWebsocketMessage;

+import websocket.drawboard.wsmessages.StringWebsocketMessage;

+

+/**

+ * A Room represents a drawboard where a number of

+ * users participate.<br><br>

+ *

+ * Note: Instance methods should only be invoked by calling

+ * {@link #invokeAndWait(Runnable)} to ensure access is correctly synchronized.

+ */

+public final class Room {

+

+    /**

+     * Specifies the type of a room message that is sent to a client.<br>

+     * Note: Currently we are sending simple string messages - for production

+     * apps, a JSON lib should be used for object-level messages.<br><br>

+     *

+     * The number (single char) will be prefixed to the string when sending

+     * the message.

+     */

+    public static enum MessageType {

+        /**

+         * '0': Error: contains error message.

+         */

+        ERROR('0'),

+        /**

+         * '1': DrawMessage: contains serialized DrawMessage(s) prefixed

+         *      with the current Player's {@link Player#lastReceivedMessageId}

+         *      and ",".<br>

+         *      Multiple draw messages are concatenated with "|" as separator.

+         */

+        DRAW_MESSAGE('1'),

+        /**

+         * '2': ImageMessage: Contains number of current players in this room.

+         *      After this message a Binary Websocket message will follow,

+         *      containing the current Room image as PNG.<br>

+         *      This is the first message that a Room sends to a new Player.

+         */

+        IMAGE_MESSAGE('2'),

+        /**

+         * '3': PlayerChanged: contains "+" or "-" which indicate a player

+         *      was added or removed to this Room.

+         */

+        PLAYER_CHANGED('3');

+

+        private final char flag;

+

+        private MessageType(char flag) {

+            this.flag = flag;

+        }

+

+    }

+

+

+    /**

+     * The lock used to synchronize access to this Room.

+     */

+    private final ReentrantLock roomLock = new ReentrantLock();

+

+    /**

+     * Indicates if this room has already been shutdown.

+     */

+    private volatile boolean closed = false;

+

+    /**

+     * If <code>true</code>, outgoing DrawMessages will be buffered until the

+     * drawmessageBroadcastTimer ticks. Otherwise they will be sent

+     * immediately.

+     */

+    private static final boolean BUFFER_DRAW_MESSAGES = true;

+

+    /**

+     * A timer which sends buffered drawmessages to the client at once

+     * at a regular interval, to avoid sending a lot of very small

+     * messages which would cause TCP overhead and high CPU usage.

+     */

+    private final Timer drawmessageBroadcastTimer = new Timer();

+

+    private static final int TIMER_DELAY = 30;

+

+    /**

+     * The current active broadcast timer task. If null, then no Broadcast task is scheduled.

+     * The Task will be scheduled if the first player enters the Room, and

+     * cancelled if the last player exits the Room, to avoid unnecessary timer executions.

+     */

+    private TimerTask activeBroadcastTimerTask;

+

+

+    /**

+     * The current image of the room drawboard. DrawMessages that are

+     * received from Players will be drawn onto this image.

+     */

+    private final BufferedImage roomImage =

+            new BufferedImage(900, 600, BufferedImage.TYPE_INT_RGB);

+    private final Graphics2D roomGraphics = roomImage.createGraphics();

+

+

+    /**

+     * The maximum number of players that can join this room.

+     */

+    private static final int MAX_PLAYER_COUNT = 100;

+

+    /**

+     * List of all currently joined players.

+     */

+    private final List<Player> players = new ArrayList<Player>();

+

+

+

+    public Room() {

+        roomGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,

+                RenderingHints.VALUE_ANTIALIAS_ON);

+

+        // Clear the image with white background.

+        roomGraphics.setBackground(Color.WHITE);

+        roomGraphics.clearRect(0, 0, roomImage.getWidth(),

+                roomImage.getHeight());

+    }

+

+    private TimerTask createBroadcastTimerTask() {

+        return new TimerTask() {

+            @Override

+            public void run() {

+                invokeAndWait(new Runnable() {

+                    @Override

+                    public void run() {

+                        broadcastTimerTick();

+                    }

+                });

+            }

+        };

+    }

+

+    /**

+     * Creates a Player from the given Client and adds it to this room.

+     * @param client the client

+     */

+    public Player createAndAddPlayer(Client client) {

+        if (players.size() >= MAX_PLAYER_COUNT) {

+            throw new IllegalStateException("Maximum player count ("

+                    + MAX_PLAYER_COUNT + ") has been reached.");

+        }

+

+        Player p = new Player(this, client);

+

+        // Broadcast to the other players that one player joined.

+        broadcastRoomMessage(MessageType.PLAYER_CHANGED, "+");

+

+        // Add the new player to the list.

+        players.add(p);

+

+        // If currently no Broadcast Timer Task is scheduled, then we need to create one.

+        if (activeBroadcastTimerTask == null) {

+            activeBroadcastTimerTask = createBroadcastTimerTask();

+            drawmessageBroadcastTimer.schedule(activeBroadcastTimerTask,

+                    TIMER_DELAY, TIMER_DELAY);

+        }

+

+        // Send him the current number of players and the current room image.

+        String content = String.valueOf(players.size());

+        p.sendRoomMessage(MessageType.IMAGE_MESSAGE, content);

+

+        // Store image as PNG

+        ByteArrayOutputStream bout = new ByteArrayOutputStream();

+        try {

+            ImageIO.write(roomImage, "PNG", bout);

+        } catch (IOException e) { /* Should never happen */ }

+

+

+        // Send the image as binary message.

+        BinaryWebsocketMessage msg = new BinaryWebsocketMessage(

+                ByteBuffer.wrap(bout.toByteArray()));

+        p.getClient().sendMessage(msg);

+

+        return p;

+

+    }

+

+    /**

+     * @see Player#removeFromRoom()

+     * @param p

+     */

+    private void internalRemovePlayer(Player p) {

+        boolean removed = players.remove(p);

+        assert removed;

+

+        // If the last player left the Room, we need to cancel the Broadcast Timer Task.

+        if (players.size() == 0) {

+            // Cancel the task.

+            // Note that it can happen that the TimerTask is just about to execute (from

+            // the Timer thread) but waits until all players are gone (or even until a new

+            // player is added to the list), and then executes. This is OK. To prevent it,

+            // a TimerTask subclass would need to have some boolean "cancel" instance variable and

+            // query it in the invocation of Room#invokeAndWait.

+            activeBroadcastTimerTask.cancel();

+            activeBroadcastTimerTask = null;

+        }

+

+        // Broadcast that one player is removed.

+        broadcastRoomMessage(MessageType.PLAYER_CHANGED, "-");

+    }

+

+    /**

+     * @see Player#handleDrawMessage(DrawMessage, long)

+     * @param p

+     * @param msg

+     * @param msgId

+     */

+    private void internalHandleDrawMessage(Player p, DrawMessage msg,

+            long msgId) {

+        p.setLastReceivedMessageId(msgId);

+

+        // Draw the RoomMessage onto our Room Image.

+        msg.draw(roomGraphics);

+

+        // Broadcast the Draw Message.

+        broadcastDrawMessage(msg);

+    }

+

+

+    /**

+     * Broadcasts the given drawboard message to all connected players.<br>

+     * Note: For DrawMessages, please use

+     * {@link #broadcastDrawMessage(DrawMessage)}

+     * as this method will buffer them and prefix them with the correct

+     * last received Message ID.

+     * @param type

+     * @param content

+     */

+    private void broadcastRoomMessage(MessageType type, String content) {

+        for (Player p : players) {

+            p.sendRoomMessage(type, content);

+        }

+    }

+

+

+    /**

+     * Broadcast the given DrawMessage. This will buffer the message

+     * and the {@link #drawmessageBroadcastTimer} will broadcast them

+     * at a regular interval, prefixing them with the player's current

+     * {@link Player#lastReceivedMessageId}.

+     * @param msg

+     */

+    private void broadcastDrawMessage(DrawMessage msg) {

+        if (!BUFFER_DRAW_MESSAGES) {

+            String msgStr = msg.toString();

+

+            for (Player p : players) {

+                String s = String.valueOf(p.getLastReceivedMessageId())

+                        + "," + msgStr;

+                p.sendRoomMessage(MessageType.DRAW_MESSAGE, s);

+            }

+        } else {

+            for (Player p : players) {

+                p.getBufferedDrawMessages().add(msg);

+            }

+        }

+    }

+

+

+    /**

+     * Tick handler for the broadcastTimer.

+     */

+    private void broadcastTimerTick() {

+        // For each Player, send all per Player buffered

+        // DrawMessages, prefixing each DrawMessage with the player's

+        // lastReceivedMessageId.

+        // Multiple messages are concatenated with "|".

+

+        for (Player p : players) {

+

+            StringBuilder sb = new StringBuilder();

+            List<DrawMessage> drawMessages = p.getBufferedDrawMessages();

+

+            if (drawMessages.size() > 0) {

+                for (int i = 0; i < drawMessages.size(); i++) {

+                    DrawMessage msg = drawMessages.get(i);

+

+                    String s = String.valueOf(p.getLastReceivedMessageId())

+                            + "," + msg.toString();

+                    if (i > 0)

+                        sb.append("|");

+

+                    sb.append(s);

+                }

+                drawMessages.clear();

+

+                p.sendRoomMessage(MessageType.DRAW_MESSAGE, sb.toString());

+            }

+        }

+    }

+

+    /**

+     * A list of cached {@link Runnable}s to prevent recursive invocation of Runnables

+     * by one thread. This variable is only used by one thread at a time and then

+     * set to <code>null</code>.

+     */

+    private List<Runnable> cachedRunnables = null;

+

+    /**

+     * Submits the given Runnable to the Room Executor and waits until it

+     * has been executed. Currently, this simply means that the Runnable

+     * will be run directly inside of a synchronized() block.<br>

+     * Note that if a runnable recursively calls invokeAndWait() with another

+     * runnable on this Room, it will not be executed recursively, but instead

+     * cached until the original runnable is finished, to keep the behavior of

+     * using a Executor.

+     * @param task

+     */

+    public void invokeAndWait(Runnable task)  {

+

+        // Check if the current thread already holds a lock on this room.

+        // If yes, then we must not directly execute the Runnable but instead

+        // cache it until the original invokeAndWait() has finished.

+        if (roomLock.isHeldByCurrentThread()) {

+

+            if (cachedRunnables == null) {

+                cachedRunnables = new ArrayList<Runnable>();

+            }

+            cachedRunnables.add(task);

+

+        } else {

+

+            roomLock.lock();

+            try {

+                // Explicitly overwrite value to ensure data consistency in

+                // current thread

+                cachedRunnables = null;

+

+                if (!closed) {

+                    task.run();

+                }

+

+                // Run the cached runnables.

+                if (cachedRunnables != null) {

+                    for (int i = 0; i < cachedRunnables.size(); i++) {

+                        if (!closed) {

+                            cachedRunnables.get(i).run();

+                        }

+                    }

+                    cachedRunnables = null;

+                }

+

+            } finally {

+                roomLock.unlock();

+            }

+

+        }

+

+    }

+

+    /**

+     * Shuts down the roomExecutor and the drawmessageBroadcastTimer.

+     */

+    public void shutdown() {

+        invokeAndWait(new Runnable() {

+            @Override

+            public void run() {

+                closed = true;

+                drawmessageBroadcastTimer.cancel();

+                roomGraphics.dispose();

+            }

+        });

+    }

+

+

+    /**

+     * A Player participates in a Room. It is the interface between the

+     * {@link Room} and the {@link Client}.<br><br>

+     *

+     * Note: This means a player object is actually a join between Room and

+     * Client.

+     */

+    public final class Player {

+

+        /**

+         * The room to which this player belongs.

+         */

+        private Room room;

+

+        /**

+         * The room buffers the last draw message ID that was received from

+         * this player.

+         */

+        private long lastReceivedMessageId = 0;

+

+        private final Client client;

+

+        /**

+         * Buffered DrawMessages that will be sent by a Timer.

+         */

+        private final List<DrawMessage> bufferedDrawMessages =

+                new ArrayList<DrawMessage>();

+

+        private List<DrawMessage> getBufferedDrawMessages() {

+            return bufferedDrawMessages;

+        }

+

+        private Player(Room room, Client client) {

+            this.room = room;

+            this.client = client;

+        }

+

+        public Room getRoom() {

+            return room;

+        }

+

+        public Client getClient() {

+            return client;

+        }

+

+        /**

+         * Removes this player from its room, e.g. when

+         * the client disconnects.

+         */

+        public void removeFromRoom() {

+            if (room != null) {

+                room.internalRemovePlayer(this);

+                room = null;

+            }

+        }

+

+

+        private long getLastReceivedMessageId() {

+            return lastReceivedMessageId;

+        }

+        private void setLastReceivedMessageId(long value) {

+            lastReceivedMessageId = value;

+        }

+

+

+        /**

+         * Handles the given DrawMessage by drawing it onto this Room's

+         * image and by broadcasting it to the connected players.

+         * @param msg

+         * @param msgId

+         */

+        public void handleDrawMessage(DrawMessage msg, long msgId) {

+            room.internalHandleDrawMessage(this, msg, msgId);

+        }

+

+

+        /**

+         * Sends the given room message.

+         * @param type

+         * @param content

+         */

+        private void sendRoomMessage(MessageType type, String content) {

+            if (content == null || type == null)

+                throw new NullPointerException();

+

+            String completeMsg = String.valueOf(type.flag) + content;

+

+            client.sendMessage(new StringWebsocketMessage(completeMsg));

+        }

+    }

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/AbstractWebsocketMessage.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/AbstractWebsocketMessage.class
new file mode 100644
index 0000000..3f4846b
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/AbstractWebsocketMessage.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/AbstractWebsocketMessage.java b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/AbstractWebsocketMessage.java
new file mode 100644
index 0000000..b1945fc
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/AbstractWebsocketMessage.java
@@ -0,0 +1,25 @@
+/*

+ *  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 websocket.drawboard.wsmessages;

+

+/**

+ * Abstract base class for Websocket Messages (binary or string)

+ * that can be buffered.

+ */

+public abstract class AbstractWebsocketMessage {

+

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/BinaryWebsocketMessage.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/BinaryWebsocketMessage.class
new file mode 100644
index 0000000..fe5a3f4
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/BinaryWebsocketMessage.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/BinaryWebsocketMessage.java b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/BinaryWebsocketMessage.java
new file mode 100644
index 0000000..0a28f64
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/BinaryWebsocketMessage.java
@@ -0,0 +1,34 @@
+/*

+ *  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 websocket.drawboard.wsmessages;

+

+import java.nio.ByteBuffer;

+

+/**

+ * Represents a binary websocket message.

+ */

+public final class BinaryWebsocketMessage extends AbstractWebsocketMessage {

+    private final ByteBuffer bytes;

+

+    public BinaryWebsocketMessage(ByteBuffer bytes) {

+        this.bytes = bytes;

+    }

+

+    public ByteBuffer getBytes() {

+        return bytes;

+    }

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/CloseWebsocketMessage.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/CloseWebsocketMessage.class
new file mode 100644
index 0000000..bad57e0
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/CloseWebsocketMessage.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/CloseWebsocketMessage.java b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/CloseWebsocketMessage.java
new file mode 100644
index 0000000..898117f
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/CloseWebsocketMessage.java
@@ -0,0 +1,24 @@
+/*

+ *  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 websocket.drawboard.wsmessages;

+

+/**

+ * Represents a "close" message that closes the session.

+ */

+public class CloseWebsocketMessage extends AbstractWebsocketMessage {

+

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/StringWebsocketMessage.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/StringWebsocketMessage.class
new file mode 100644
index 0000000..acf3928
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/StringWebsocketMessage.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/StringWebsocketMessage.java b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/StringWebsocketMessage.java
new file mode 100644
index 0000000..8646ea7
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/drawboard/wsmessages/StringWebsocketMessage.java
@@ -0,0 +1,34 @@
+/*

+ *  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 websocket.drawboard.wsmessages;

+

+/**

+ * Represents a string websocket message.

+ *

+ */

+public final class StringWebsocketMessage extends AbstractWebsocketMessage {

+    private final String string;

+

+    public StringWebsocketMessage(String string) {

+        this.string = string;

+    }

+

+    public String getString() {

+        return string;

+    }

+

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/echo/EchoAnnotation.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/echo/EchoAnnotation.class
new file mode 100644
index 0000000..8c03f33
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/echo/EchoAnnotation.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/echo/EchoAnnotation.java b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/echo/EchoAnnotation.java
new file mode 100644
index 0000000..b592153
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/echo/EchoAnnotation.java
@@ -0,0 +1,70 @@
+/*

+ *  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 websocket.echo;

+

+import java.io.IOException;

+import java.nio.ByteBuffer;

+

+import javax.websocket.OnMessage;

+import javax.websocket.PongMessage;

+import javax.websocket.Session;

+import javax.websocket.server.ServerEndpoint;

+

+@ServerEndpoint("/websocket/echoAnnotation")

+public class EchoAnnotation {

+

+    @OnMessage

+    public void echoTextMessage(Session session, String msg, boolean last) {

+        try {

+            if (session.isOpen()) {

+                session.getBasicRemote().sendText(msg, last);

+            }

+        } catch (IOException e) {

+            try {

+                session.close();

+            } catch (IOException e1) {

+                // Ignore

+            }

+        }

+    }

+

+    @OnMessage

+    public void echoBinaryMessage(Session session, ByteBuffer bb,

+            boolean last) {

+        try {

+            if (session.isOpen()) {

+                session.getBasicRemote().sendBinary(bb, last);

+            }

+        } catch (IOException e) {

+            try {

+                session.close();

+            } catch (IOException e1) {

+                // Ignore

+            }

+        }

+    }

+

+    /**

+     * Process a received pong. This is a NO-OP.

+     *

+     * @param pm    Ignored.

+     */

+    @OnMessage

+    public void echoPongMessage(PongMessage pm) {

+        // NO-OP

+    }

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/echo/EchoEndpoint$1.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/echo/EchoEndpoint$1.class
new file mode 100644
index 0000000..18684e5
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/echo/EchoEndpoint$1.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/echo/EchoEndpoint$EchoMessageHandlerBinary.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/echo/EchoEndpoint$EchoMessageHandlerBinary.class
new file mode 100644
index 0000000..62bf22d
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/echo/EchoEndpoint$EchoMessageHandlerBinary.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/echo/EchoEndpoint$EchoMessageHandlerText.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/echo/EchoEndpoint$EchoMessageHandlerText.class
new file mode 100644
index 0000000..c7ca81c
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/echo/EchoEndpoint$EchoMessageHandlerText.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/echo/EchoEndpoint.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/echo/EchoEndpoint.class
new file mode 100644
index 0000000..4a304db
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/echo/EchoEndpoint.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/echo/EchoEndpoint.java b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/echo/EchoEndpoint.java
new file mode 100644
index 0000000..d503994
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/echo/EchoEndpoint.java
@@ -0,0 +1,80 @@
+/*

+ *  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 websocket.echo;

+

+import java.io.IOException;

+import java.nio.ByteBuffer;

+

+import javax.websocket.Endpoint;

+import javax.websocket.EndpointConfig;

+import javax.websocket.MessageHandler;

+import javax.websocket.RemoteEndpoint;

+import javax.websocket.Session;

+

+public class EchoEndpoint extends Endpoint {

+

+    @Override

+    public void onOpen(Session session, EndpointConfig endpointConfig) {

+        RemoteEndpoint.Basic remoteEndpointBasic = session.getBasicRemote();

+        session.addMessageHandler(new EchoMessageHandlerText(remoteEndpointBasic));

+        session.addMessageHandler(new EchoMessageHandlerBinary(remoteEndpointBasic));

+    }

+

+    private static class EchoMessageHandlerText

+            implements MessageHandler.Partial<String> {

+

+        private final RemoteEndpoint.Basic remoteEndpointBasic;

+

+        private EchoMessageHandlerText(RemoteEndpoint.Basic remoteEndpointBasic) {

+            this.remoteEndpointBasic = remoteEndpointBasic;

+        }

+

+        @Override

+        public void onMessage(String message, boolean last) {

+            try {

+                if (remoteEndpointBasic != null) {

+                    remoteEndpointBasic.sendText(message, last);

+                }

+            } catch (IOException e) {

+                // TODO Auto-generated catch block

+                e.printStackTrace();

+            }

+        }

+    }

+    

+    private static class EchoMessageHandlerBinary

+            implements MessageHandler.Partial<ByteBuffer> {

+

+        private final RemoteEndpoint.Basic remoteEndpointBasic;

+

+        private EchoMessageHandlerBinary(RemoteEndpoint.Basic remoteEndpointBasic) {

+            this.remoteEndpointBasic = remoteEndpointBasic;

+        }

+        

+        @Override

+        public void onMessage(ByteBuffer message, boolean last) {

+            try {

+                if (remoteEndpointBasic != null) {

+                    remoteEndpointBasic.sendBinary(message, last);

+                }

+            } catch (IOException e) {

+                // TODO Auto-generated catch block

+                e.printStackTrace();

+            }

+        }

+    }

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/Direction.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/Direction.class
new file mode 100644
index 0000000..b6f4494
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/Direction.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/Direction.java b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/Direction.java
new file mode 100644
index 0000000..b36c7a2
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/Direction.java
@@ -0,0 +1,21 @@
+/*

+ * 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 websocket.snake;

+

+public enum Direction {

+    NONE, NORTH, SOUTH, EAST, WEST

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/Location$1.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/Location$1.class
new file mode 100644
index 0000000..2b42186
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/Location$1.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/Location.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/Location.class
new file mode 100644
index 0000000..e9db638
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/Location.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/Location.java b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/Location.java
new file mode 100644
index 0000000..d5c0007
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/Location.java
@@ -0,0 +1,65 @@
+/*

+ * 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 websocket.snake;

+

+public class Location {

+

+    public int x;

+    public int y;

+

+    public Location(int x, int y) {

+        this.x = x;

+        this.y = y;

+    }

+

+    public Location getAdjacentLocation(Direction direction) {

+        switch (direction) {

+            case NORTH:

+                return new Location(x, y - SnakeAnnotation.GRID_SIZE);

+            case SOUTH:

+                return new Location(x, y + SnakeAnnotation.GRID_SIZE);

+            case EAST:

+                return new Location(x + SnakeAnnotation.GRID_SIZE, y);

+            case WEST:

+                return new Location(x - SnakeAnnotation.GRID_SIZE, y);

+            case NONE:

+                // fall through

+            default:

+                return this;

+        }

+    }

+

+    @Override

+    public boolean equals(Object o) {

+        if (this == o) return true;

+        if (o == null || getClass() != o.getClass()) return false;

+

+        Location location = (Location) o;

+

+        if (x != location.x) return false;

+        if (y != location.y) return false;

+

+        return true;

+    }

+

+    @Override

+    public int hashCode() {

+        int result = x;

+        result = 31 * result + y;

+        return result;

+    }

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/Snake.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/Snake.class
new file mode 100644
index 0000000..1449f3c
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/Snake.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/Snake.java b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/Snake.java
new file mode 100644
index 0000000..f2c9c4c
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/Snake.java
@@ -0,0 +1,150 @@
+/*

+ * 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 websocket.snake;

+

+import java.io.IOException;

+import java.util.ArrayDeque;

+import java.util.Collection;

+import java.util.Deque;

+

+import javax.websocket.CloseReason;

+import javax.websocket.CloseReason.CloseCodes;

+import javax.websocket.Session;

+

+public class Snake {

+

+    private static final int DEFAULT_LENGTH = 5;

+

+    private final int id;

+    private final Session session;

+

+    private Direction direction;

+    private int length = DEFAULT_LENGTH;

+    private Location head;

+    private final Deque<Location> tail = new ArrayDeque<Location>();

+    private final String hexColor;

+

+    public Snake(int id, Session session) {

+        this.id = id;

+        this.session = session;

+        this.hexColor = SnakeAnnotation.getRandomHexColor();

+        resetState();

+    }

+

+    private void resetState() {

+        this.direction = Direction.NONE;

+        this.head = SnakeAnnotation.getRandomLocation();

+        this.tail.clear();

+        this.length = DEFAULT_LENGTH;

+    }

+

+    private synchronized void kill() {

+        resetState();

+        sendMessage("{'type': 'dead'}");

+    }

+

+    private synchronized void reward() {

+        length++;

+        sendMessage("{'type': 'kill'}");

+    }

+

+

+    protected void sendMessage(String msg) {

+        try {

+            session.getBasicRemote().sendText(msg);

+        } catch (IOException ioe) {

+            CloseReason cr =

+                    new CloseReason(CloseCodes.CLOSED_ABNORMALLY, ioe.getMessage());

+            try {

+                session.close(cr);

+            } catch (IOException ioe2) {

+                // Ignore

+            }

+        }

+    }

+

+    public synchronized void update(Collection<Snake> snakes) {

+        Location nextLocation = head.getAdjacentLocation(direction);

+        if (nextLocation.x >= SnakeAnnotation.PLAYFIELD_WIDTH) {

+            nextLocation.x = 0;

+        }

+        if (nextLocation.y >= SnakeAnnotation.PLAYFIELD_HEIGHT) {

+            nextLocation.y = 0;

+        }

+        if (nextLocation.x < 0) {

+            nextLocation.x = SnakeAnnotation.PLAYFIELD_WIDTH;

+        }

+        if (nextLocation.y < 0) {

+            nextLocation.y = SnakeAnnotation.PLAYFIELD_HEIGHT;

+        }

+        if (direction != Direction.NONE) {

+            tail.addFirst(head);

+            if (tail.size() > length) {

+                tail.removeLast();

+            }

+            head = nextLocation;

+        }

+

+        handleCollisions(snakes);

+    }

+

+    private void handleCollisions(Collection<Snake> snakes) {

+        for (Snake snake : snakes) {

+            boolean headCollision = id != snake.id && snake.getHead().equals(head);

+            boolean tailCollision = snake.getTail().contains(head);

+            if (headCollision || tailCollision) {

+                kill();

+                if (id != snake.id) {

+                    snake.reward();

+                }

+            }

+        }

+    }

+

+    public synchronized Location getHead() {

+        return head;

+    }

+

+    public synchronized Collection<Location> getTail() {

+        return tail;

+    }

+

+    public synchronized void setDirection(Direction direction) {

+        this.direction = direction;

+    }

+

+    public synchronized String getLocationsJson() {

+        StringBuilder sb = new StringBuilder();

+        sb.append(String.format("{x: %d, y: %d}",

+                Integer.valueOf(head.x), Integer.valueOf(head.y)));

+        for (Location location : tail) {

+            sb.append(',');

+            sb.append(String.format("{x: %d, y: %d}",

+                    Integer.valueOf(location.x), Integer.valueOf(location.y)));

+        }

+        return String.format("{'id':%d,'body':[%s]}",

+                Integer.valueOf(id), sb.toString());

+    }

+

+    public int getId() {

+        return id;

+    }

+

+    public String getHexColor() {

+        return hexColor;

+    }

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/SnakeAnnotation.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/SnakeAnnotation.class
new file mode 100644
index 0000000..57b9447
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/SnakeAnnotation.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/SnakeAnnotation.java b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/SnakeAnnotation.java
new file mode 100644
index 0000000..519a304
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/SnakeAnnotation.java
@@ -0,0 +1,135 @@
+/*

+ * 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 websocket.snake;

+

+import java.awt.Color;

+import java.io.EOFException;

+import java.util.Iterator;

+import java.util.Random;

+import java.util.concurrent.atomic.AtomicInteger;

+

+import javax.websocket.OnClose;

+import javax.websocket.OnError;

+import javax.websocket.OnMessage;

+import javax.websocket.OnOpen;

+import javax.websocket.Session;

+import javax.websocket.server.ServerEndpoint;

+

+@ServerEndpoint(value = "/websocket/snake")

+public class SnakeAnnotation {

+

+    public static final int PLAYFIELD_WIDTH = 640;

+    public static final int PLAYFIELD_HEIGHT = 480;

+    public static final int GRID_SIZE = 10;

+

+    private static final AtomicInteger snakeIds = new AtomicInteger(0);

+    private static final Random random = new Random();

+

+

+    private final int id;

+    private Snake snake;

+

+    public static String getRandomHexColor() {

+        float hue = random.nextFloat();

+        // sat between 0.1 and 0.3

+        float saturation = (random.nextInt(2000) + 1000) / 10000f;

+        float luminance = 0.9f;

+        Color color = Color.getHSBColor(hue, saturation, luminance);

+        return '#' + Integer.toHexString(

+                (color.getRGB() & 0xffffff) | 0x1000000).substring(1);

+    }

+

+

+    public static Location getRandomLocation() {

+        int x = roundByGridSize(random.nextInt(PLAYFIELD_WIDTH));

+        int y = roundByGridSize(random.nextInt(PLAYFIELD_HEIGHT));

+        return new Location(x, y);

+    }

+

+

+    private static int roundByGridSize(int value) {

+        value = value + (GRID_SIZE / 2);

+        value = value / GRID_SIZE;

+        value = value * GRID_SIZE;

+        return value;

+    }

+

+    public SnakeAnnotation() {

+        this.id = snakeIds.getAndIncrement();

+    }

+

+

+    @OnOpen

+    public void onOpen(Session session) {

+        this.snake = new Snake(id, session);

+        SnakeTimer.addSnake(snake);

+        StringBuilder sb = new StringBuilder();

+        for (Iterator<Snake> iterator = SnakeTimer.getSnakes().iterator();

+                iterator.hasNext();) {

+            Snake snake = iterator.next();

+            sb.append(String.format("{id: %d, color: '%s'}",

+                    Integer.valueOf(snake.getId()), snake.getHexColor()));

+            if (iterator.hasNext()) {

+                sb.append(',');

+            }

+        }

+        SnakeTimer.broadcast(String.format("{'type': 'join','data':[%s]}",

+                sb.toString()));

+    }

+

+

+    @OnMessage

+    public void onTextMessage(String message) {

+        if ("west".equals(message)) {

+            snake.setDirection(Direction.WEST);

+        } else if ("north".equals(message)) {

+            snake.setDirection(Direction.NORTH);

+        } else if ("east".equals(message)) {

+            snake.setDirection(Direction.EAST);

+        } else if ("south".equals(message)) {

+            snake.setDirection(Direction.SOUTH);

+        }

+    }

+

+

+    @OnClose

+    public void onClose() {

+        SnakeTimer.removeSnake(snake);

+        SnakeTimer.broadcast(String.format("{'type': 'leave', 'id': %d}",

+                Integer.valueOf(id)));

+    }

+

+

+    @OnError

+    public void onError(Throwable t) throws Throwable {

+        // Most likely cause is a user closing their browser. Check to see if

+        // the root cause is EOF and if it is ignore it.

+        // Protect against infinite loops.

+        int count = 0;

+        Throwable root = t;

+        while (root.getCause() != null && count < 20) {

+            root = root.getCause();

+            count ++;

+        }

+        if (root instanceof EOFException) {

+            // Assume this is triggered by the user closing their browser and

+            // ignore it.

+        } else {

+            throw t;

+        }

+    }

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/SnakeTimer$1.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/SnakeTimer$1.class
new file mode 100644
index 0000000..1fb6005
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/SnakeTimer$1.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/SnakeTimer.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/SnakeTimer.class
new file mode 100644
index 0000000..14693fc
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/SnakeTimer.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/SnakeTimer.java b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/SnakeTimer.java
new file mode 100644
index 0000000..abc4991
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/snake/SnakeTimer.java
@@ -0,0 +1,115 @@
+/*

+ * 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 websocket.snake;

+

+import java.util.Collection;

+import java.util.Collections;

+import java.util.Iterator;

+import java.util.Timer;

+import java.util.TimerTask;

+import java.util.concurrent.ConcurrentHashMap;

+

+import org.apache.juli.logging.Log;

+import org.apache.juli.logging.LogFactory;

+

+/**

+ * Sets up the timer for the multi-player snake game WebSocket example.

+ */

+public class SnakeTimer {

+

+    private static final Log log =

+            LogFactory.getLog(SnakeTimer.class);

+

+    private static Timer gameTimer = null;

+

+    private static final long TICK_DELAY = 100;

+

+    private static final ConcurrentHashMap<Integer, Snake> snakes =

+            new ConcurrentHashMap<Integer, Snake>();

+

+    protected static synchronized void addSnake(Snake snake) {

+        if (snakes.size() == 0) {

+            startTimer();

+        }

+        snakes.put(Integer.valueOf(snake.getId()), snake);

+    }

+

+

+    protected static Collection<Snake> getSnakes() {

+        return Collections.unmodifiableCollection(snakes.values());

+    }

+

+

+    protected static synchronized void removeSnake(Snake snake) {

+        snakes.remove(Integer.valueOf(snake.getId()));

+        if (snakes.size() == 0) {

+            stopTimer();

+        }

+    }

+

+

+    protected static void tick() {

+        StringBuilder sb = new StringBuilder();

+        for (Iterator<Snake> iterator = SnakeTimer.getSnakes().iterator();

+                iterator.hasNext();) {

+            Snake snake = iterator.next();

+            snake.update(SnakeTimer.getSnakes());

+            sb.append(snake.getLocationsJson());

+            if (iterator.hasNext()) {

+                sb.append(',');

+            }

+        }

+        broadcast(String.format("{'type': 'update', 'data' : [%s]}",

+                sb.toString()));

+    }

+

+    protected static void broadcast(String message) {

+        for (Snake snake : SnakeTimer.getSnakes()) {

+            try {

+                snake.sendMessage(message);

+            } catch (IllegalStateException ise) {

+                // An ISE can occur if an attempt is made to write to a

+                // WebSocket connection after it has been closed. The

+                // alternative to catching this exception is to synchronise

+                // the writes to the clients along with the addSnake() and

+                // removeSnake() methods that are already synchronised.

+            }

+        }

+    }

+

+

+    public static void startTimer() {

+        gameTimer = new Timer(SnakeTimer.class.getSimpleName() + " Timer");

+        gameTimer.scheduleAtFixedRate(new TimerTask() {

+            @Override

+            public void run() {

+                try {

+                    tick();

+                } catch (RuntimeException e) {

+                    log.error("Caught to prevent timer from shutting down", e);

+                }

+            }

+        }, TICK_DELAY, TICK_DELAY);

+    }

+

+

+    public static void stopTimer() {

+        if (gameTimer != null) {

+            gameTimer.cancel();

+        }

+    }

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/chat/ChatWebSocketServlet$1.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/chat/ChatWebSocketServlet$1.class
new file mode 100644
index 0000000..9bbb179
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/chat/ChatWebSocketServlet$1.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/chat/ChatWebSocketServlet$ChatMessageInbound.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/chat/ChatWebSocketServlet$ChatMessageInbound.class
new file mode 100644
index 0000000..3eecb9e
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/chat/ChatWebSocketServlet$ChatMessageInbound.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/chat/ChatWebSocketServlet.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/chat/ChatWebSocketServlet.class
new file mode 100644
index 0000000..edf666f
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/chat/ChatWebSocketServlet.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/chat/ChatWebSocketServlet.java b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/chat/ChatWebSocketServlet.java
new file mode 100644
index 0000000..12d5419
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/chat/ChatWebSocketServlet.java
@@ -0,0 +1,105 @@
+/*

+ * 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 websocket.tc7.chat;

+

+import java.io.IOException;

+import java.nio.ByteBuffer;

+import java.nio.CharBuffer;

+import java.util.Set;

+import java.util.concurrent.CopyOnWriteArraySet;

+import java.util.concurrent.atomic.AtomicInteger;

+

+import javax.servlet.http.HttpServletRequest;

+

+import org.apache.catalina.websocket.MessageInbound;

+import org.apache.catalina.websocket.StreamInbound;

+import org.apache.catalina.websocket.WebSocketServlet;

+import org.apache.catalina.websocket.WsOutbound;

+

+import util.HTMLFilter;

+

+/**

+ * Example web socket servlet for chat.

+ * @deprecated See {@link websocket.chat.ChatAnnotation}

+ */

+@Deprecated

+public class ChatWebSocketServlet extends WebSocketServlet {

+

+    private static final long serialVersionUID = 1L;

+

+    private static final String GUEST_PREFIX = "Guest";

+

+    private final AtomicInteger connectionIds = new AtomicInteger(0);

+    private final Set<ChatMessageInbound> connections =

+            new CopyOnWriteArraySet<ChatMessageInbound>();

+

+    @Override

+    protected StreamInbound createWebSocketInbound(String subProtocol,

+            HttpServletRequest request) {

+        return new ChatMessageInbound(connectionIds.incrementAndGet());

+    }

+

+    private final class ChatMessageInbound extends MessageInbound {

+

+        private final String nickname;

+

+        private ChatMessageInbound(int id) {

+            this.nickname = GUEST_PREFIX + id;

+        }

+

+        @Override

+        protected void onOpen(WsOutbound outbound) {

+            connections.add(this);

+            String message = String.format("* %s %s",

+                    nickname, "has joined.");

+            broadcast(message);

+        }

+

+        @Override

+        protected void onClose(int status) {

+            connections.remove(this);

+            String message = String.format("* %s %s",

+                    nickname, "has disconnected.");

+            broadcast(message);

+        }

+

+        @Override

+        protected void onBinaryMessage(ByteBuffer message) throws IOException {

+            throw new UnsupportedOperationException(

+                    "Binary message not supported.");

+        }

+

+        @Override

+        protected void onTextMessage(CharBuffer message) throws IOException {

+            // Never trust the client

+            String filteredMessage = String.format("%s: %s",

+                    nickname, HTMLFilter.filter(message.toString()));

+            broadcast(filteredMessage);

+        }

+

+        private void broadcast(String message) {

+            for (ChatMessageInbound connection : connections) {

+                try {

+                    CharBuffer buffer = CharBuffer.wrap(message);

+                    connection.getWsOutbound().writeTextMessage(buffer);

+                } catch (IOException ignore) {

+                    // Ignore

+                }

+            }

+        }

+    }

+}
\ No newline at end of file
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/echo/EchoMessage$EchoMessageInbound.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/echo/EchoMessage$EchoMessageInbound.class
new file mode 100644
index 0000000..ddb9dc4
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/echo/EchoMessage$EchoMessageInbound.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/echo/EchoMessage.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/echo/EchoMessage.class
new file mode 100644
index 0000000..d600882
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/echo/EchoMessage.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/echo/EchoMessage.java b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/echo/EchoMessage.java
new file mode 100644
index 0000000..4073306
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/echo/EchoMessage.java
@@ -0,0 +1,88 @@
+/*

+ * 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 websocket.tc7.echo;

+

+import java.io.IOException;

+import java.nio.ByteBuffer;

+import java.nio.CharBuffer;

+

+import javax.servlet.ServletException;

+import javax.servlet.http.HttpServletRequest;

+

+import org.apache.catalina.websocket.MessageInbound;

+import org.apache.catalina.websocket.StreamInbound;

+import org.apache.catalina.websocket.WebSocketServlet;

+/**

+ * @deprecated See {@link websocket.echo.EchoAnnotation}

+ */

+@Deprecated

+public class EchoMessage extends WebSocketServlet {

+

+    private static final long serialVersionUID = 1L;

+    private volatile int byteBufSize;

+    private volatile int charBufSize;

+

+    @Override

+    public void init() throws ServletException {

+        super.init();

+        byteBufSize = getInitParameterIntValue("byteBufferMaxSize", 2097152);

+        charBufSize = getInitParameterIntValue("charBufferMaxSize", 2097152);

+    }

+

+    public int getInitParameterIntValue(String name, int defaultValue) {

+        String val = this.getInitParameter(name);

+        int result;

+        if(null != val) {

+            try {

+                result = Integer.parseInt(val);

+            }catch (Exception x) {

+                result = defaultValue;

+            }

+        } else {

+            result = defaultValue;

+        }

+

+        return result;

+    }

+

+

+

+    @Override

+    protected StreamInbound createWebSocketInbound(String subProtocol,

+            HttpServletRequest request) {

+        return new EchoMessageInbound(byteBufSize,charBufSize);

+    }

+

+    private static final class EchoMessageInbound extends MessageInbound {

+

+        public EchoMessageInbound(int byteBufferMaxSize, int charBufferMaxSize) {

+            super();

+            setByteBufferMaxSize(byteBufferMaxSize);

+            setCharBufferMaxSize(charBufferMaxSize);

+        }

+

+        @Override

+        protected void onBinaryMessage(ByteBuffer message) throws IOException {

+            getWsOutbound().writeBinaryMessage(message);

+        }

+

+        @Override

+        protected void onTextMessage(CharBuffer message) throws IOException {

+            getWsOutbound().writeTextMessage(message);

+        }

+    }

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/echo/EchoStream$1.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/echo/EchoStream$1.class
new file mode 100644
index 0000000..b770d79
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/echo/EchoStream$1.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/echo/EchoStream$EchoStreamInbound.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/echo/EchoStream$EchoStreamInbound.class
new file mode 100644
index 0000000..478607d
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/echo/EchoStream$EchoStreamInbound.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/echo/EchoStream.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/echo/EchoStream.class
new file mode 100644
index 0000000..f3d819d
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/echo/EchoStream.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/echo/EchoStream.java b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/echo/EchoStream.java
new file mode 100644
index 0000000..9b34302
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/echo/EchoStream.java
@@ -0,0 +1,73 @@
+/*

+ * 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 websocket.tc7.echo;

+

+import java.io.IOException;

+import java.io.InputStream;

+import java.io.Reader;

+

+import javax.servlet.http.HttpServletRequest;

+

+import org.apache.catalina.websocket.StreamInbound;

+import org.apache.catalina.websocket.WebSocketServlet;

+import org.apache.catalina.websocket.WsOutbound;

+

+/**

+ * @deprecated See {@link websocket.echo.EchoAnnotation}

+ */

+@Deprecated

+public class EchoStream extends WebSocketServlet {

+

+    private static final long serialVersionUID = 1L;

+

+    @Override

+    protected StreamInbound createWebSocketInbound(String subProtocol,

+            HttpServletRequest request) {

+        return new EchoStreamInbound();

+    }

+

+    private static final class EchoStreamInbound extends StreamInbound {

+

+        @Override

+        protected void onBinaryData(InputStream is) throws IOException {

+            // Simply echo the data to back to the client.

+            WsOutbound outbound = getWsOutbound();

+

+            int i = is.read();

+            while (i != -1) {

+                outbound.writeBinaryData(i);

+                i = is.read();

+            }

+

+            outbound.flush();

+        }

+

+        @Override

+        protected void onTextData(Reader r) throws IOException {

+            // Simply echo the data to back to the client.

+            WsOutbound outbound = getWsOutbound();

+

+            int c = r.read();

+            while (c != -1) {

+                outbound.writeTextData((char) c);

+                c = r.read();

+            }

+

+            outbound.flush();

+        }

+    }

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/Direction.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/Direction.class
new file mode 100644
index 0000000..7db9c55
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/Direction.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/Direction.java b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/Direction.java
new file mode 100644
index 0000000..4e375e7
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/Direction.java
@@ -0,0 +1,25 @@
+/*

+ * 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 websocket.tc7.snake;

+

+/**

+ * @deprecated See {@link websocket.snake.Direction}

+ */

+@Deprecated

+public enum Direction {

+    NONE, NORTH, SOUTH, EAST, WEST

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/Location$1.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/Location$1.class
new file mode 100644
index 0000000..7e71c10
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/Location$1.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/Location.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/Location.class
new file mode 100644
index 0000000..b052a15
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/Location.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/Location.java b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/Location.java
new file mode 100644
index 0000000..257752e
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/Location.java
@@ -0,0 +1,69 @@
+/*

+ * 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 websocket.tc7.snake;

+

+/**

+ * @deprecated See {@link websocket.snake.Location}

+ */

+@Deprecated

+public class Location {

+

+    public int x;

+    public int y;

+

+    public Location(int x, int y) {

+        this.x = x;

+        this.y = y;

+    }

+

+    public Location getAdjacentLocation(Direction direction) {

+        switch (direction) {

+            case NORTH:

+                return new Location(x, y - SnakeWebSocketServlet.GRID_SIZE);

+            case SOUTH:

+                return new Location(x, y + SnakeWebSocketServlet.GRID_SIZE);

+            case EAST:

+                return new Location(x + SnakeWebSocketServlet.GRID_SIZE, y);

+            case WEST:

+                return new Location(x - SnakeWebSocketServlet.GRID_SIZE, y);

+            case NONE:

+                // fall through

+            default:

+                return this;

+        }

+    }

+

+    @Override

+    public boolean equals(Object o) {

+        if (this == o) return true;

+        if (o == null || getClass() != o.getClass()) return false;

+

+        Location location = (Location) o;

+

+        if (x != location.x) return false;

+        if (y != location.y) return false;

+

+        return true;

+    }

+

+    @Override

+    public int hashCode() {

+        int result = x;

+        result = 31 * result + y;

+        return result;

+    }

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/Snake.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/Snake.class
new file mode 100644
index 0000000..336a9c3
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/Snake.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/Snake.java b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/Snake.java
new file mode 100644
index 0000000..df36332
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/Snake.java
@@ -0,0 +1,148 @@
+/*

+ * 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 websocket.tc7.snake;

+

+import java.io.IOException;

+import java.nio.CharBuffer;

+import java.util.ArrayDeque;

+import java.util.Collection;

+import java.util.Deque;

+

+import org.apache.catalina.websocket.WsOutbound;

+

+/**

+ * @deprecated See {@link websocket.snake.Snake}

+ */

+@Deprecated

+public class Snake {

+

+    private static final int DEFAULT_LENGTH = 5;

+

+    private final int id;

+    private final WsOutbound outbound;

+

+    private Direction direction;

+    private int length = DEFAULT_LENGTH;

+    private Location head;

+    private Deque<Location> tail = new ArrayDeque<Location>();

+    private String hexColor;

+

+    public Snake(int id, WsOutbound outbound) {

+        this.id = id;

+        this.outbound = outbound;

+        this.hexColor = SnakeWebSocketServlet.getRandomHexColor();

+        resetState();

+    }

+

+    private void resetState() {

+        this.direction = Direction.NONE;

+        this.head = SnakeWebSocketServlet.getRandomLocation();

+        this.tail.clear();

+        this.length = DEFAULT_LENGTH;

+    }

+

+    private synchronized void kill() {

+        resetState();

+        try {

+            CharBuffer response = CharBuffer.wrap("{'type': 'dead'}");

+            outbound.writeTextMessage(response);

+        } catch (IOException ioe) {

+            // Ignore

+        }

+    }

+

+    private synchronized void reward() {

+        length++;

+        try {

+            CharBuffer response = CharBuffer.wrap("{'type': 'kill'}");

+            outbound.writeTextMessage(response);

+        } catch (IOException ioe) {

+            // Ignore

+        }

+    }

+

+    public synchronized void update(Collection<Snake> snakes) {

+        Location nextLocation = head.getAdjacentLocation(direction);

+        if (nextLocation.x >= SnakeWebSocketServlet.PLAYFIELD_WIDTH) {

+            nextLocation.x = 0;

+        }

+        if (nextLocation.y >= SnakeWebSocketServlet.PLAYFIELD_HEIGHT) {

+            nextLocation.y = 0;

+        }

+        if (nextLocation.x < 0) {

+            nextLocation.x = SnakeWebSocketServlet.PLAYFIELD_WIDTH;

+        }

+        if (nextLocation.y < 0) {

+            nextLocation.y = SnakeWebSocketServlet.PLAYFIELD_HEIGHT;

+        }

+        if (direction != Direction.NONE) {

+            tail.addFirst(head);

+            if (tail.size() > length) {

+                tail.removeLast();

+            }

+            head = nextLocation;

+        }

+

+        handleCollisions(snakes);

+    }

+

+    private void handleCollisions(Collection<Snake> snakes) {

+        for (Snake snake : snakes) {

+            boolean headCollision = id != snake.id && snake.getHead().equals(head);

+            boolean tailCollision = snake.getTail().contains(head);

+            if (headCollision || tailCollision) {

+                kill();

+                if (id != snake.id) {

+                    snake.reward();

+                }

+            }

+        }

+    }

+

+    public synchronized Location getHead() {

+        return head;

+    }

+

+    public synchronized Collection<Location> getTail() {

+        return tail;

+    }

+

+    public synchronized void setDirection(Direction direction) {

+        this.direction = direction;

+    }

+

+    public synchronized String getLocationsJson() {

+        StringBuilder sb = new StringBuilder();

+        sb.append(String.format("{x: %d, y: %d}",

+                Integer.valueOf(head.x), Integer.valueOf(head.y)));

+        for (Location location : tail) {

+            sb.append(',');

+            sb.append(String.format("{x: %d, y: %d}",

+                    Integer.valueOf(location.x), Integer.valueOf(location.y)));

+        }

+        return String.format("{'id':%d,'body':[%s]}",

+                Integer.valueOf(id), sb.toString());

+    }

+

+    public int getId() {

+        return id;

+    }

+

+    public String getHexColor() {

+        return hexColor;

+    }

+}

diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/SnakeWebSocketServlet$1.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/SnakeWebSocketServlet$1.class
new file mode 100644
index 0000000..2707bb7
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/SnakeWebSocketServlet$1.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/SnakeWebSocketServlet$SnakeMessageInbound.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/SnakeWebSocketServlet$SnakeMessageInbound.class
new file mode 100644
index 0000000..2649169
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/SnakeWebSocketServlet$SnakeMessageInbound.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/SnakeWebSocketServlet.class b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/SnakeWebSocketServlet.class
new file mode 100644
index 0000000..f174143
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/SnakeWebSocketServlet.class
Binary files differ
diff --git a/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/SnakeWebSocketServlet.java b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/SnakeWebSocketServlet.java
new file mode 100644
index 0000000..0a8c353
--- /dev/null
+++ b/tomcat-uid/webapps/examples/WEB-INF/classes/websocket/tc7/snake/SnakeWebSocketServlet.java
@@ -0,0 +1,215 @@
+/*

+ * 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 websocket.tc7.snake;

+

+import java.awt.Color;

+import java.io.IOException;

+import java.nio.ByteBuffer;

+import java.nio.CharBuffer;

+import java.util.Collection;

+import java.util.Collections;

+import java.util.Iterator;

+import java.util.Random;

+import java.util.Timer;

+import java.util.TimerTask;

+import java.util.concurrent.ConcurrentHashMap;

+import java.util.concurrent.atomic.AtomicInteger;

+

+import javax.servlet.ServletException;

+import javax.servlet.http.HttpServletRequest;

+

+import org.apache.catalina.websocket.MessageInbound;

+import org.apache.catalina.websocket.StreamInbound;

+import org.apache.catalina.websocket.WebSocketServlet;

+import org.apache.catalina.websocket.WsOutbound;

+import org.apache.juli.logging.Log;

+import org.apache.juli.logging.LogFactory;

+

+/**

+ * Example web socket servlet for simple multi-player snake.

+ * @deprecated See {@link websocket.snake.SnakeAnnotation}

+ */

+@Deprecated

+public class SnakeWebSocketServlet extends WebSocketServlet {

+

+    private static final long serialVersionUID = 1L;

+

+    private static final Log log =

+            LogFactory.getLog(SnakeWebSocketServlet.class);

+

+    public static final int PLAYFIELD_WIDTH = 640;

+    public static final int PLAYFIELD_HEIGHT = 480;

+    public static final int GRID_SIZE = 10;

+

+    private static final long TICK_DELAY = 100;

+

+    private static final Random random = new Random();

+

+    private final Timer gameTimer =

+            new Timer(SnakeWebSocketServlet.class.getSimpleName() + " Timer");

+

+    private final AtomicInteger connectionIds = new AtomicInteger(0);

+    private final ConcurrentHashMap<Integer, Snake> snakes =

+            new ConcurrentHashMap<Integer, Snake>();

+    private final ConcurrentHashMap<Integer, SnakeMessageInbound> connections =

+            new ConcurrentHashMap<Integer, SnakeMessageInbound>();

+

+    @Override

+    public void init() throws ServletException {

+        super.init();

+        gameTimer.scheduleAtFixedRate(new TimerTask() {

+            @Override

+            public void run() {

+                try {

+                    tick();

+                } catch (RuntimeException e) {

+                    log.error("Caught to prevent timer from shutting down", e);

+                }

+            }

+        }, TICK_DELAY, TICK_DELAY);

+    }

+

+    private void tick() {

+        StringBuilder sb = new StringBuilder();

+        for (Iterator<Snake> iterator = getSnakes().iterator();

+                iterator.hasNext();) {

+            Snake snake = iterator.next();

+            snake.update(getSnakes());

+            sb.append(snake.getLocationsJson());

+            if (iterator.hasNext()) {

+                sb.append(',');

+            }

+        }

+        broadcast(String.format("{'type': 'update', 'data' : [%s]}",

+                sb.toString()));

+    }

+

+    private void broadcast(String message) {

+        for (SnakeMessageInbound connection : getConnections()) {

+            try {

+                CharBuffer buffer = CharBuffer.wrap(message);

+                connection.getWsOutbound().writeTextMessage(buffer);

+            } catch (IOException ignore) {

+                // Ignore

+            }

+        }

+    }

+

+    private Collection<SnakeMessageInbound> getConnections() {

+        return Collections.unmodifiableCollection(connections.values());

+    }

+

+    private Collection<Snake> getSnakes() {

+        return Collections.unmodifiableCollection(snakes.values());

+    }

+

+    public static String getRandomHexColor() {

+        float hue = random.nextFloat();

+        // sat between 0.1 and 0.3

+        float saturation = (random.nextInt(2000) + 1000) / 10000f;

+        float luminance = 0.9f;

+        Color color = Color.getHSBColor(hue, saturation, luminance);

+        return '#' + Integer.toHexString(

+                (color.getRGB() & 0xffffff) | 0x1000000).substring(1);

+    }

+

+    public static Location getRandomLocation() {

+        int x = roundByGridSize(

+                random.nextInt(SnakeWebSocketServlet.PLAYFIELD_WIDTH));

+        int y = roundByGridSize(

+                random.nextInt(SnakeWebSocketServlet.PLAYFIELD_HEIGHT));

+        return new Location(x, y);

+    }

+

+    private static int roundByGridSize(int value) {

+        value = value + (SnakeWebSocketServlet.GRID_SIZE / 2);

+        value = value / SnakeWebSocketServlet.GRID_SIZE;

+        value = value * SnakeWebSocketServlet.GRID_SIZE;

+        return value;

+    }

+

+    @Override

+    public void destroy() {

+        super.destroy();

+        if (gameTimer != null) {

+            gameTimer.cancel();

+        }

+    }

+

+    @Override

+    protected StreamInbound createWebSocketInbound(String subProtocol,

+            HttpServletRequest request) {

+        return new SnakeMessageInbound(connectionIds.incrementAndGet());

+    }

+

+    private final class SnakeMessageInbound extends MessageInbound {

+

+        private final int id;

+        private Snake snake;

+

+        private SnakeMessageInbound(int id) {

+            this.id = id;

+        }

+

+        @Override

+        protected void onOpen(WsOutbound outbound) {

+            this.snake = new Snake(id, outbound);

+            snakes.put(Integer.valueOf(id), snake);

+            connections.put(Integer.valueOf(id), this);

+            StringBuilder sb = new StringBuilder();

+            for (Iterator<Snake> iterator = getSnakes().iterator();

+                    iterator.hasNext();) {

+                Snake snake = iterator.next();

+                sb.append(String.format("{id: %d, color: '%s'}",

+                        Integer.valueOf(snake.getId()), snake.getHexColor()));

+                if (iterator.hasNext()) {

+                    sb.append(',');

+                }

+            }

+            broadcast(String.format("{'type': 'join','data':[%s]}",

+                    sb.toString()));

+        }

+

+        @Override

+        protected void onClose(int status) {

+            connections.remove(Integer.valueOf(id));

+            snakes.remove(Integer.valueOf(id));

+            broadcast(String.format("{'type': 'leave', 'id': %d}",

+                    Integer.valueOf(id)));

+        }

+

+        @Override

+        protected void onBinaryMessage(ByteBuffer message) throws IOException {

+            throw new UnsupportedOperationException(

+                    "Binary message not supported.");

+        }

+

+        @Override

+        protected void onTextMessage(CharBuffer charBuffer) throws IOException {

+            String message = charBuffer.toString();

+            if ("west".equals(message)) {

+                snake.setDirection(Direction.WEST);

+            } else if ("north".equals(message)) {

+                snake.setDirection(Direction.NORTH);

+            } else if ("east".equals(message)) {

+                snake.setDirection(Direction.EAST);

+            } else if ("south".equals(message)) {

+                snake.setDirection(Direction.SOUTH);

+            }

+        }

+    }

+}