This commit is contained in:
2024-11-30 19:03:49 +08:00
commit 1e6763c160
3806 changed files with 737676 additions and 0 deletions

View File

@@ -0,0 +1,141 @@
/*
* 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.
*/
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 javax.servlet.http.HttpSession;
import util.CookieFilter;
import util.HTMLFilter;
/**
* Example servlet showing request headers
*
* @author James Duncan Davidson <duncan@eng.sun.com>
*/
public class CookieExample extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
ResourceBundle rb = ResourceBundle.getBundle("LocalStrings",request.getLocale());
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");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE html><html>");
out.println("<head>");
out.println("<meta charset=\"UTF-8\" />");
String title = rb.getString("cookies.title");
out.println("<title>" + title + "</title>");
out.println("</head>");
out.println("<body bgcolor=\"white\">");
// relative links
// XXX
// making these absolute till we work out the
// 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>");
out.println("<a href=\"../index.html\">");
out.println("<img src=\"../images/return.gif\" height=24 " +
"width=24 align=right border=0 alt=\"return\"></a>");
out.println("<h3>" + title + "</h3>");
Cookie[] cookies = request.getCookies();
if ((cookies != null) && (cookies.length > 0)) {
HttpSession session = request.getSession(false);
String sessionId = null;
if (session != null) {
sessionId = session.getId();
}
out.println(rb.getString("cookies.cookies") + "<br>");
for (int i = 0; i < cookies.length; i++) {
Cookie cookie = cookies[i];
String cName = cookie.getName();
String cValue = cookie.getValue();
out.print("Cookie Name: " + HTMLFilter.filter(cName) + "<br>");
out.println(" Cookie Value: "
+ HTMLFilter.filter(CookieFilter.filter(cName, cValue, sessionId))
+ "<br><br>");
}
} else {
out.println(rb.getString("cookies.no-cookies"));
}
if (aCookie != null) {
out.println("<P>");
out.println(rb.getString("cookies.set") + "<br>");
out.print(rb.getString("cookies.name") + " "
+ HTMLFilter.filter(cookieName) + "<br>");
out.print(rb.getString("cookies.value") + " "
+ HTMLFilter.filter(cookieValue));
}
out.println("<P>");
out.println(rb.getString("cookies.make-cookie") + "<br>");
out.print("<form action=\"");
out.println("CookieExample\" method=POST>");
out.print(rb.getString("cookies.name") + " ");
out.println("<input type=text length=20 name=cookiename><br>");
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
{
doGet(request, response);
}
}

View File

@@ -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.
*/
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;
/**
* The simplest possible servlet.
*
* @author James Duncan Davidson
*/
public class HelloWorldExample extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
ResourceBundle rb =
ResourceBundle.getBundle("LocalStrings",request.getLocale());
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE html><html>");
out.println("<head>");
out.println("<meta charset=\"UTF-8\" />");
String title = rb.getString("helloworld.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 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("<img src=\"../images/code.gif\" height=24 " +
"width=24 align=right border=0 alt=\"view code\"></a>");
out.println("<a href=\"../index.html\">");
out.println("<img src=\"../images/return.gif\" height=24 " +
"width=24 align=right border=0 alt=\"return\"></a>");
out.println("<h1>" + title + "</h1>");
out.println("</body>");
out.println("</html>");
}
}

View File

@@ -0,0 +1,48 @@
# 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.
cookies.cookies=Your browser is sending the following cookies:
cookies.make-cookie=Create a cookie to send to your browser
cookies.name=Name:
cookies.no-cookies=Your browser isn't sending any cookies
cookies.set=You just sent the following cookie to your browser:
cookies.title=Cookies Example
cookies.value=Value:
helloworld.title=Hello World!
requestheader.title=Request Header Example
requestinfo.label.method=Method:
requestinfo.label.pathinfo=Path Info:
requestinfo.label.protocol=Protocol:
requestinfo.label.remoteaddr=Remote Address:
requestinfo.label.requesturi=Request URI:
requestinfo.title=Request Information Example
requestparams.firstname=First Name:
requestparams.lastname=Last Name:
requestparams.no-params=No Parameters, Please enter some
requestparams.params-in-req=Parameters in this request:
requestparams.title=Request Parameters Example
sessions.adddata=Add data to your session
sessions.created=Created:
sessions.data=The following data is in your session:
sessions.dataname=Name of Session Attribute:
sessions.datavalue=Value of Session Attribute:
sessions.id=Session ID:
sessions.lastaccessed=Last Accessed:
sessions.title=Sessions Example

View File

@@ -0,0 +1,28 @@
# 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.
cookies.make-cookie=Erzeuge ein Cookie um es an deinen Browser zu senden
cookies.name=Name:
requestheader.title=Request-Header Beispiel
requestinfo.label.protocol=Protokoll:
requestinfo.label.requesturi=Anfrage-URI:
requestparams.firstname=Vorname:
requestparams.no-params=Keine Parameter, bitte geben Sie welche ein
requestparams.title=Beispiel für Anfrageparameter
sessions.title=Sessions-Beispiel

View File

@@ -0,0 +1,48 @@
# 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.
cookies.cookies=Tu navegador está enviando los siguientes cookies:
cookies.make-cookie=Crea un cookie para enviarlo a tu navegador
cookies.name=Nombre:
cookies.no-cookies=Tu navegador no está enviando cookies
cookies.set=Acabas de enviar a tu navegador estos cookies:
cookies.title=Ejemplo de Cookies
cookies.value=Valor:
helloworld.title=Hola Mundo!
requestheader.title=Ejemplo de Cabecera de Requerimiento:
requestinfo.label.method=Método:
requestinfo.label.pathinfo=Info de Ruta:
requestinfo.label.protocol=Protocolo:
requestinfo.label.remoteaddr=Direccion Remota:
requestinfo.label.requesturi=URI de Requerimiento:
requestinfo.title=Ejemplo de Informacion de Requerimiento:
requestparams.firstname=Nombre:
requestparams.lastname=Apellidos:
requestparams.no-params=No hay párametro. Por favor, usa alguno
requestparams.params-in-req=Parámetros en este Request:
requestparams.title=Ejemplo de solicitud con parámetros:
sessions.adddata=Añade datos a tu sesión:
sessions.created=Creado:
sessions.data=Lo siguientes datos están en tu sesión:
sessions.dataname=Nombre del atributo de sesión:
sessions.datavalue=Valor del atributo de sesión:
sessions.id=ID de Sesión:
sessions.lastaccessed=Ultimo Acceso:
sessions.title=Ejemplo de Sesiones

View File

@@ -0,0 +1,48 @@
# 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.
cookies.cookies=Votre navigateur retourne les cookies suivant:
cookies.make-cookie=Création d'un cookie à retourner à votre navigateur
cookies.name=Nom:
cookies.no-cookies=Votre navigateur ne retourne aucun cookie
cookies.set=Vous venez d'envoyer le cookie suivant à votre navigateur:
cookies.title=Exemple d'utilisation de Cookies
cookies.value=Valeur:
helloworld.title=Salut le Monde!
requestheader.title=Exemple d'information sur les entêtes de requête
requestinfo.label.method=Méthode:
requestinfo.label.pathinfo=Info de chemin:
requestinfo.label.protocol=Protocole:
requestinfo.label.remoteaddr=Adresse distante:
requestinfo.label.requesturi=URI de requête:
requestinfo.title=Exemple d'information sur la requête
requestparams.firstname=Prénom:
requestparams.lastname=Nom:
requestparams.no-params=Pas de paramêtre, merci d'en saisir quelques-uns
requestparams.params-in-req=Paramêtres dans la requête:
requestparams.title=Exemple de requête avec paramètres
sessions.adddata=Ajouter des données à votre session
sessions.created=Crée le:
sessions.data=Les données existantes dans votre session:
sessions.dataname=Nom de l'Attribut de Session:
sessions.datavalue=Valeur de l'Attribut de Session:
sessions.id=ID de Session:
sessions.lastaccessed=Dernier accès:
sessions.title=Exemple de Sessions

View File

@@ -0,0 +1,48 @@
# 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.
cookies.cookies=あなたののブラウザから次のCookieが送信されています
cookies.make-cookie=ブラウザへ送信する cookie を作成します。
cookies.name=Name:
cookies.no-cookies=あなたのブラウザはクッキーを送信していません。
cookies.set=ブラウザに cookie を送信しました。
cookies.title=Cookie 例
cookies.value=値:
helloworld.title=Hello World!
requestheader.title=リクエストヘッダ例
requestinfo.label.method=メソッド:
requestinfo.label.pathinfo=パス情報:
requestinfo.label.protocol=プロトコル:
requestinfo.label.remoteaddr=リモートアドレス:
requestinfo.label.requesturi=Request URI:
requestinfo.title=リクエスト情報例
requestparams.firstname=First Name:
requestparams.lastname=\ Last Name:
requestparams.no-params=パラメータがありません。何か入力してください。
requestparams.params-in-req=このリクエストのパラメータ:
requestparams.title=リクエストパラメータ例
sessions.adddata=セッションにデータを追加します
sessions.created=作成:
sessions.data=あなたのセッションには次のデータがあります:
sessions.dataname=セッション属性名:
sessions.datavalue=セッション属性の値:
sessions.id=セッションID
sessions.lastaccessed=最終アクセス:
sessions.title=セッション例

View File

@@ -0,0 +1,48 @@
# 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.
cookies.cookies=귀하의 브라우저가 다음 쿠키들을 보냅니다.
cookies.make-cookie=귀하의 브라우저에 전송하기 위한 쿠키 생성
cookies.name=이름:
cookies.no-cookies=귀하의 브라우저는 어떤 쿠키도 전송하지 않습니다.
cookies.set=귀하는 다음 쿠키를, 귀하의 브라우저에 전송했습니다.
cookies.title=쿠키들의 예제
cookies.value=값:
helloworld.title=안녕 세계여!
requestheader.title=요청의 헤더 예제
requestinfo.label.method=메소드:
requestinfo.label.pathinfo=경로 정보:
requestinfo.label.protocol=프로토콜:
requestinfo.label.remoteaddr=원격 주소:
requestinfo.label.requesturi=요청 URI:
requestinfo.title=요청 정보 예제
requestparams.firstname=이름:
requestparams.lastname=
requestparams.no-params=파라미터들이 없습니다. 파라미터들을 입력하십시오.
requestparams.params-in-req=이 요청의 파라미터들:
requestparams.title=요청 파라미터들의 예제
sessions.adddata=귀하의 세션에 데이터를 추가
sessions.created=생성시간:
sessions.data=귀하의 세션에 다음 데이터가 있습니다:
sessions.dataname=세션 속성 이름:
sessions.datavalue=세션 속성 값:
sessions.id=세션 ID:
sessions.lastaccessed=최종 접근 시간:
sessions.title=세션들의 예제

View File

@@ -0,0 +1,48 @@
# 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.
cookies.cookies=O se browser esta a enviar os seguintes cookies:
cookies.make-cookie=Crie um cookie para enviar para o seu browser
cookies.name=Nome:
cookies.no-cookies=O seu browser nao esta a enviar nenhuns cookies
cookies.set=Acabou de enviar o seguinte cookie para o seu browser:
cookies.title=CExamplo de Cookies
cookies.value=Valor:
helloworld.title=Ola Mundo!
requestheader.title=Exemplo da Cebeceira do Pedido
requestinfo.label.method=Metodo:
requestinfo.label.pathinfo=Informacao do Caminho:
requestinfo.label.protocol=Protocolo:
requestinfo.label.remoteaddr=Endereco Remoto:
requestinfo.label.requesturi=URI do Pedido:
requestinfo.title=Exemplo da Informacao do Pedido
requestparams.firstname=Primeiro Nome:
requestparams.lastname=Apelido:
requestparams.no-params=Sem Parametros, Por favor entre alguns
requestparams.params-in-req=Parametros neste pedido:
requestparams.title=Examplo de Parametros do Pedido
sessions.adddata=Adicione data a sua sessao
sessions.created=Criada:
sessions.data=Os seguintes dados fazem parte da sua sessao:
sessions.dataname=Nome do atributo da sessao:
sessions.datavalue=Valor do atributo da Sessao:
sessions.id=Identificador da Sessao:
sessions.lastaccessed=Ultima vez acedida:
sessions.title=Examplo de sessoes

View File

@@ -0,0 +1,16 @@
# 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.
requestparams.title=Пример параметров запроса

View File

@@ -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.
cookies.make-cookie=创建一个发送到你的浏览器的cookie
cookies.name=名.称:
cookies.no-cookies=你的浏览器未发送任何cookie
cookies.set=你刚刚将以下cookie发送到你的浏览器
helloworld.title=你好,世界.
requestheader.title=请求 Header 示例
requestinfo.label.method=方法:
requestinfo.label.protocol=协议:
requestinfo.label.remoteaddr=远程地址:
requestinfo.label.requesturi=请求 URI (:
requestinfo.title=请求信息范例
requestparams.firstname=姓:
requestparams.no-params=没有参数,请输入一些
requestparams.params-in-req=参数在请求中.
requestparams.title=请求参数示例
sessions.adddata=将数据添加到你的会话中
sessions.id=会话ID
sessions.lastaccessed=最后访问:
sessions.title=会话.示例

View File

@@ -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.
*/
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;
/**
* Example servlet showing request headers
*
* @author James Duncan Davidson <duncan@eng.sun.com>
*/
public class RequestHeaderExample extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
ResourceBundle rb = ResourceBundle.getBundle("LocalStrings",request.getLocale());
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE html><html>");
out.println("<head>");
out.println("<meta charset=\"UTF-8\" />");
String title = rb.getString("requestheader.title");
out.println("<title>" + title + "</title>");
out.println("</head>");
out.println("<body bgcolor=\"white\">");
// all links relative
// XXX
// making these absolute till we work out the
// 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>");
out.println("<a href=\"../index.html\">");
out.println("<img src=\"../images/return.gif\" height=24 " +
"width=24 align=right border=0 alt=\"return\"></a>");
out.println("<h3>" + title + "</h3>");
out.println("<table border=0>");
Enumeration<String> e = request.getHeaderNames();
while (e.hasMoreElements()) {
String headerName = e.nextElement();
String headerValue = request.getHeader(headerName);
out.println("<tr><td bgcolor=\"#CCCCCC\">");
out.println(HTMLFilter.filter(headerName));
out.println("</td><td>");
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
{
doGet(request, response);
}
}

View File

@@ -0,0 +1,118 @@
/*
* 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.
*/
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;
/**
* Example servlet showing request information.
*
* @author James Duncan Davidson <duncan@eng.sun.com>
*/
public class RequestInfoExample extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
ResourceBundle rb = ResourceBundle.getBundle("LocalStrings",request.getLocale());
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE html><html>");
out.println("<head>");
out.println("<meta charset=\"UTF-8\" />");
String title = rb.getString("requestinfo.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!
// XXX
// making these absolute till we work out the
// addition of a PathInfo issue
out.println("<a href=\"../reqinfo.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\">");
out.println("<img src=\"../images/return.gif\" height=24 " +
"width=24 align=right border=0 alt=\"return\"></a>");
out.println("<h3>" + title + "</h3>");
out.println("<table border=0><tr><td>");
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("</td><td>");
out.println(HTMLFilter.filter(request.getRequestURI()));
out.println("</td></tr><tr><td>");
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("</td><td>");
out.println(HTMLFilter.filter(request.getPathInfo()));
out.println("</td></tr><tr><td>");
out.println(rb.getString("requestinfo.label.remoteaddr"));
out.println("</td><td>");
out.println(HTMLFilter.filter(request.getRemoteAddr()));
out.println("</td></tr>");
String cipherSuite=
(String)request.getAttribute("javax.servlet.request.cipher_suite");
if(cipherSuite!=null){
out.println("<tr><td>");
out.println("SSLCipherSuite:");
out.println("</td><td>");
out.println(HTMLFilter.filter(cipherSuite));
out.println("</td></tr>");
}
out.println("</table>");
}
@Override
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
doGet(request, response);
}
}

View File

@@ -0,0 +1,111 @@
/*
* 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.
*/
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;
/**
* Example servlet showing request headers
*
* @author James Duncan Davidson <duncan@eng.sun.com>
*/
public class RequestParamExample extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
ResourceBundle rb = ResourceBundle.getBundle("LocalStrings",request.getLocale());
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE html><html>");
out.println("<head>");
out.println("<meta charset=\"UTF-8\" />");
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
// XXX
// making these absolute till we work out the
// 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>");
out.println("<a href=\"../index.html\">");
out.println("<img src=\"../images/return.gif\" height=24 " +
"width=24 align=right border=0 alt=\"return\"></a>");
out.println("<h3>" + title + "</h3>");
String firstName = request.getParameter("firstname");
String lastName = request.getParameter("lastname");
out.println(rb.getString("requestparams.params-in-req") + "<br>");
if (firstName != null || lastName != null) {
out.println(rb.getString("requestparams.firstname"));
out.println(" = " + HTMLFilter.filter(firstName) + "<br>");
out.println(rb.getString("requestparams.lastname"));
out.println(" = " + HTMLFilter.filter(lastName));
} else {
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("<input type=text size=20 name=firstname>");
out.println("<br>");
out.println(rb.getString("requestparams.lastname"));
out.println("<input type=text size=20 name=lastname>");
out.println("<br>");
out.println("<input type=submit>");
out.println("</form>");
out.println("</body>");
out.println("</html>");
}
@Override
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
doGet(request, response);
}
}

View File

@@ -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.
*/
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) {
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 ();
}
}
}

View File

@@ -0,0 +1,147 @@
/*
* 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.
*/
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;
/**
* Example servlet showing request headers
*
* @author James Duncan Davidson <duncan@eng.sun.com>
*/
public class SessionExample extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
ResourceBundle rb = ResourceBundle.getBundle("LocalStrings",request.getLocale());
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE html><html>");
out.println("<head>");
out.println("<meta charset=\"UTF-8\" />");
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!
// XXX
// making these absolute till we work out the
// 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>");
out.println("<a href=\"../index.html\">");
out.println("<img src=\"../images/return.gif\" height=24 " +
"width=24 align=right border=0 alt=\"return\"></a>");
out.println("<h3>" + title + "</h3>");
HttpSession session = request.getSession(true);
out.println(rb.getString("sessions.id") + " " + session.getId());
out.println("<br>");
out.println(rb.getString("sessions.created") + " ");
out.println(new Date(session.getCreationTime()) + "<br>");
out.println(rb.getString("sessions.lastaccessed") + " ");
out.println(new Date(session.getLastAccessedTime()));
String dataName = request.getParameter("dataname");
String dataValue = request.getParameter("datavalue");
if (dataName != null && dataValue != null) {
session.setAttribute(dataName, dataValue);
}
out.println("<P>");
out.println(rb.getString("sessions.data") + "<br>");
Enumeration<String> names = session.getAttributeNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
String value = session.getAttribute(name).toString();
out.println(HTMLFilter.filter(name) + " = "
+ HTMLFilter.filter(value) + "<br>");
}
out.println("<P>");
out.print("<form action=\"");
out.print(response.encodeURL("SessionExample"));
out.print("\" ");
out.println("method=POST>");
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("<input type=text size=20 name=datavalue>");
out.println("<br>");
out.println("<input type=submit>");
out.println("</form>");
out.println("<P>GET based form:<br>");
out.print("<form action=\"");
out.print(response.encodeURL("SessionExample"));
out.print("\" ");
out.println("method=GET>");
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("<input type=text size=20 name=datavalue>");
out.println("<br>");
out.println("<input type=submit>");
out.println("</form>");
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
{
doGet(request, response);
}
}

View File

@@ -0,0 +1,71 @@
/*
* 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.text.SimpleDateFormat;
import java.util.Date;
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());
Date date = new Date(System.currentTimeMillis());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
resp.getWriter().write("Async dispatch worked: " + sdf.format(date) + "\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();
}
}
}

View File

@@ -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();
}
}

View File

@@ -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 async;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
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.");
Date date = new Date(System.currentTimeMillis());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
actx.getResponse().getWriter().write(
"Output from background thread. Time: " + sdf.format(date) + "\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();
}
}

View File

@@ -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");
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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 javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/*
* Ensures the Stockticker is shut down cleanly when the context stops. This
* also covers the case when the server shuts down.
*/
public class AsyncStockContextListener implements ServletContextListener {
public static final String STOCK_TICKER_KEY = "StockTicker";
@Override
public void contextInitialized(ServletContextEvent sce) {
Stockticker stockticker = new Stockticker();
ServletContext sc = sce.getServletContext();
sc.setAttribute(STOCK_TICKER_KEY, stockticker);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
ServletContext sc = sce.getServletContext();
Stockticker stockticker = (Stockticker) sc.getAttribute(STOCK_TICKER_KEY);
stockticker.shutdown();
}
}

View File

@@ -0,0 +1,149 @@
/*
* 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.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.ServletContext;
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;
import async.Stockticker.Stock;
import async.Stockticker.TickListener;
public class AsyncStockServlet extends HttpServlet implements TickListener, AsyncListener{
private static final long serialVersionUID = 1L;
private static final Log log = LogFactory.getLog(AsyncStockServlet.class);
private static final ConcurrentLinkedQueue<AsyncContext> clients =
new ConcurrentLinkedQueue<>();
private static final AtomicInteger clientcount = new AtomicInteger(0);
public AsyncStockServlet() {
log.info("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) {
Stockticker ticker = (Stockticker) req.getServletContext().getAttribute(
AsyncStockContextListener.STOCK_TICKER_KEY);
ticker.addTickListener(this);
}
} else {
new Exception("Async Not Supported").printStackTrace();
resp.sendError(400,"Async is not supported.");
}
}
@Override
public void tick(Stock stock) {
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) throws IOException {
HttpServletResponse response = (HttpServletResponse)actx.getResponse();
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();
}
@Override
public void shutdown() {
// The web application is shutting down. Complete any AsyncContexts
// associated with an active client.
Iterator<AsyncContext> it = clients.iterator();
while (it.hasNext()) {
AsyncContext actx = it.next();
try {
actx.complete();
} catch (Exception e) {
// Ignore. The async error handling will deal with this.
}
}
}
@Override
public void onComplete(AsyncEvent event) throws IOException {
if (clients.remove(event.getAsyncContext()) && clientcount.decrementAndGet()==0) {
ServletContext sc = event.getAsyncContext().getRequest().getServletContext();
Stockticker ticker = (Stockticker) sc.getAttribute(
AsyncStockContextListener.STOCK_TICKER_KEY);
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
}
}

View File

@@ -0,0 +1,207 @@
/*
* 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.List;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
public class Stockticker implements Runnable {
public volatile boolean run = true;
protected final AtomicInteger counter = new AtomicInteger(0);
final List<TickListener> listeners = new CopyOnWriteArrayList<>();
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() {
// On context stop this can be called multiple times.
// NO-OP is the ticker thread is not set
// (i.e. stop() has already completed)
if (ticker == null) {
return;
}
run = false;
try {
ticker.join();
}catch (InterruptedException x) {
Thread.interrupted();
}
ticker = null;
}
public void shutdown() {
// Notify each listener of the shutdown. This enables them to
// trigger any necessary clean-up.
for (TickListener l : listeners) {
l.shutdown();
}
// Wait for the thread to stop. This prevents warnings in the logs
// that the thread is still active when the context stops.
stop();
}
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 void shutdown();
}
public static final class Stock implements Cloneable {
protected static final DecimalFormat df = new DecimalFormat("0.00");
protected final 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;
}
}
}

View File

@@ -0,0 +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.
*/
package cal;
import java.util.Hashtable;
import javax.servlet.http.HttpServletRequest;
public class Entries {
private final 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 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);
}
}
}

View File

@@ -0,0 +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.
*/
package cal;
public class Entry {
final String hour;
String description;
public Entry(String hour) {
this.hour = hour;
this.description = "";
}
public String getHour() {
return this.hour;
}
public String getColor() {
if (description.equals("")) {
return "lightblue";
}
return "red";
}
public String getDescription() {
if (description.equals("")) {
return "None";
}
return this.description;
}
public void setDescription(String descr) {
description = descr;
}
}

View File

@@ -0,0 +1,151 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cal;
import java.util.Calendar;
import java.util.Date;
public class JspCalendar {
final Calendar calendar;
public JspCalendar() {
calendar = Calendar.getInstance();
Date trialTime = new Date();
calendar.setTime(trialTime);
}
public int getYear() {
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];
}
public String getDay() {
int x = getDayOfWeek();
String[] days = new String[] {"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"};
if (x > 7)
return "Unknown to Man";
return days[x - 1];
}
public int getMonthInt() {
return 1 + calendar.get(Calendar.MONTH);
}
public String getDate() {
return getMonthInt() + "/" + getDayOfMonth() + "/" + getYear();
}
public String getCurrentDate() {
Date dt = new Date ();
calendar.setTime (dt);
return getMonthInt() + "/" + getDayOfMonth() + "/" + getYear();
}
public String getNextDate() {
calendar.set (Calendar.DAY_OF_MONTH, getDayOfMonth() + 1);
return getDate ();
}
public String getPrevDate() {
calendar.set (Calendar.DAY_OF_MONTH, getDayOfMonth() - 1);
return getDate ();
}
public String getTime() {
return getHour() + ":" + getMinute() + ":" + getSecond();
}
public int getDayOfMonth() {
return calendar.get(Calendar.DAY_OF_MONTH);
}
public int getDayOfYear() {
return calendar.get(Calendar.DAY_OF_YEAR);
}
public int getWeekOfYear() {
return calendar.get(Calendar.WEEK_OF_YEAR);
}
public int getWeekOfMonth() {
return calendar.get(Calendar.WEEK_OF_MONTH);
}
public int getDayOfWeek() {
return calendar.get(Calendar.DAY_OF_WEEK);
}
public int getHour() {
return calendar.get(Calendar.HOUR_OF_DAY);
}
public int getMinute() {
return calendar.get(Calendar.MINUTE);
}
public int getSecond() {
return calendar.get(Calendar.SECOND);
}
public int getEra() {
return calendar.get(Calendar.ERA);
}
public String getUSTimeZone() {
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);
}
public int getDSTOffset() {
return calendar.get(Calendar.DST_OFFSET)/(60*60*1000);
}
public int getAMPM() {
return calendar.get(Calendar.AM_PM);
}
}

View File

@@ -0,0 +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.
*/
package cal;
import java.util.Hashtable;
import javax.servlet.http.HttpServletRequest;
public class TableBean {
final Hashtable<String, Entries> table;
final 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;
}
// 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;
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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 checkbox;
public class CheckTest {
String b[] = new String[] { "1", "2", "3", "4" };
public String[] getFruit() {
return b;
}
public void setFruit(String [] b) {
this.b = b;
}
}

View File

@@ -0,0 +1,113 @@
/*
* 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 colors;
public class ColorGameBean {
private String background = "yellow";
private String foreground = "red";
private String color1 = foreground;
private String color2 = background;
private String hint = "no";
private int attempts = 0;
private int intval = 0;
private boolean tookHints = false;
public void processRequest() {
// background = "yellow";
// foreground = "red";
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;
}
}
attempts++;
}
public void setColor2(String x) {
color2 = x;
}
public void setColor1(String x) {
color1 = x;
}
public void setAction(String x) {
if (!tookHints)
tookHints = x.equalsIgnoreCase("Hint");
hint = x;
}
public String getColor2() {
return background;
}
public String getColor1() {
return foreground;
}
public int getAttempts() {
return attempts;
}
public boolean getHint() {
return hint.equalsIgnoreCase("Hint");
}
public boolean getSuccess() {
if (background.equalsIgnoreCase("black") ||
background.equalsIgnoreCase("cyan")) {
if (foreground.equalsIgnoreCase("black") ||
foreground.equalsIgnoreCase("cyan")) {
return true;
}
return false;
}
return false;
}
public boolean getHintTaken() {
return tookHints;
}
public void reset() {
foreground = "red";
background = "yellow";
}
public void setIntval(int value) {
intval = value;
}
public int getIntval() {
return intval;
}
}

View File

@@ -0,0 +1,240 @@
/*
* 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 compressionFilters;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.StringTokenizer;
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.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.
*
* @author Amy Roh
* @author Dmitri Valdin
*/
public class CompressionFilter implements Filter {
/**
* Minimal reasonable threshold.
*/
private static final int MIN_THRESHOLD = 128;
/**
* Minimal reasonable buffer.
*/
// 8KB is what tomcat would use by default anyway
private static final int MIN_BUFFER = 8192;
/**
* The threshold number to compress.
*/
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 for this filter.
*/
private int debug = 0;
/**
* Place this filter into service.
*
* @param filterConfig The filter configuration object
*/
@Override
public void init(FilterConfig filterConfig) {
if (filterConfig != null) {
String value = filterConfig.getInitParameter("debug");
if (value!=null) {
debug = Integer.parseInt(value);
}
String str = filterConfig.getInitParameter("compressionThreshold");
if (str != null) {
compressionThreshold = Integer.parseInt(str);
if (compressionThreshold != 0 && compressionThreshold < MIN_THRESHOLD) {
if (debug > 0) {
System.out.println("compressionThreshold should be either 0 - no compression or >= " + MIN_THRESHOLD);
System.out.println("compressionThreshold set to " + MIN_THRESHOLD);
}
compressionThreshold = MIN_THRESHOLD;
}
}
str = filterConfig.getInitParameter("compressionBuffer");
if (str != null) {
compressionBuffer = Integer.parseInt(str);
if (compressionBuffer < MIN_BUFFER) {
if (debug > 0) {
System.out.println("compressionBuffer should be >= " + MIN_BUFFER);
System.out.println("compressionBuffer set to " + MIN_BUFFER);
}
compressionBuffer = MIN_BUFFER;
}
}
str = filterConfig.getInitParameter("compressionMimeTypes");
if (str != null) {
List<String> values = new ArrayList<>();
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 " +
Arrays.toString(compressionMimeTypes));
}
}
}
}
/**
* Take this filter out of service.
*/
@Override
public void destroy() {
}
/**
* The <code>doFilter</code> method of the Filter is called by the container
* each time a request/response pair is passed through the chain due
* to a client request for a resource at the end of the chain.
* The FilterChain passed into this method allows the Filter to pass on the
* request and response to the next entity in the chain.<p>
* This method first examines the request to check whether the client support
* compression. <br>
* It simply just pass the request and response if there is no support for
* compression.<br>
* If the compression support is available, it creates a
* CompressionServletResponseWrapper object which compresses the content and
* modifies the header if the content length is big enough.
* 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 {
if (debug > 0) {
System.out.println("@doFilter");
}
if (compressionThreshold == 0) {
if (debug > 0) {
System.out.println("doFilter got called, but compressionThreshold is set to 0 - no compression");
}
chain.doFilter(request, response);
return;
}
boolean supportCompression = false;
if (request instanceof HttpServletRequest) {
if (debug > 1) {
System.out.println("requestURI = " + ((HttpServletRequest)request).getRequestURI());
}
// Are we allowed to compress ?
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");
}
chain.doFilter(request, response);
return;
}
Enumeration<String> e =
((HttpServletRequest)request).getHeaders("Accept-Encoding");
while (e.hasMoreElements()) {
String name = e.nextElement();
if (name.indexOf("gzip") != -1) {
if (debug > 0) {
System.out.println("supports compression");
}
supportCompression = true;
} else {
if (debug > 0) {
System.out.println("no support for compression");
}
}
}
}
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");
}
try {
chain.doFilter(request, wrappedResponse);
} finally {
wrappedResponse.finishResponse();
}
return;
}
} else {
if (debug > 0) {
System.out.println("doFilter gets called w/o compression");
}
chain.doFilter(request, response);
return;
}
}
}

View File

@@ -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 compressionFilters;
import java.io.IOException;
import java.util.Enumeration;
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
* @author Amy Roh
*/
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<String> e = request.getHeaders("Accept-Encoding");
while (e.hasMoreElements()) {
String name = e.nextElement();
out.println(name);
if (name.indexOf("gzip") != -1) {
out.println("gzip supported -- able to compress");
}
else {
out.println("gzip not supported");
}
}
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();
}
}

View File

@@ -0,0 +1,435 @@
/*
* 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 compressionFilters;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.zip.GZIPOutputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
/**
* Implementation of <b>ServletOutputStream</b> that works with
* the CompressionServletResponseWrapper implementation.
*
* @author Amy Roh
* @author Dmitri Valdin
*/
public class CompressionResponseStream extends ServletOutputStream {
// ----------------------------------------------------------- Constructors
/**
* Construct a servlet output stream associated with the specified Response.
*
* @param responseWrapper The associated response wrapper
* @param originalOutput the output stream
*/
public CompressionResponseStream(
CompressionServletResponseWrapper responseWrapper,
ServletOutputStream originalOutput) {
super();
closed = false;
this.response = responseWrapper;
this.output = originalOutput;
}
// ----------------------------------------------------- Instance Variables
/**
* The threshold number which decides to compress or not.
* Users can configure in web.xml to set it to fit their needs.
*/
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;
/**
* The buffer through which all of our output bytes are passed.
*/
protected byte[] buffer = null;
/**
* The number of data bytes currently in the buffer.
*/
protected int bufferCount = 0;
/**
* The underlying gzip output stream to which we should write data.
*/
protected OutputStream gzipstream = null;
/**
* Has this stream been closed?
*/
protected boolean closed = false;
/**
* The response with which this servlet output stream is associated.
*/
protected final CompressionServletResponseWrapper response;
/**
* The underlying servlet output stream to which we should write data.
*/
protected final ServletOutputStream output;
// --------------------------------------------------------- Public Methods
/**
* Set debug level.
*
* @param debug The higher the number, the more detail shown. Currently the
* range 0 (none) to 3 (everything) is used.
*/
public void setDebugLevel(int debug) {
this.debug = debug;
}
/**
* Set the compressionThreshold number and create buffer for this size
*/
protected void setCompressionThreshold(int compressionThreshold) {
this.compressionThreshold = compressionThreshold;
buffer = new byte[this.compressionThreshold];
if (debug > 1) {
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.
*
* @param compressionMimeTypes The mimetypes that will be compressed.
*/
public void setCompressionMimeTypes(String[] compressionMimeTypes) {
this.compressionMimeTypes = compressionMimeTypes;
if (debug > 1) {
System.out.println("compressionMimeTypes is set to " +
Arrays.toString(this.compressionMimeTypes));
}
}
/**
* 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) {
System.out.println("close() @ CompressionResponseStream");
}
if (closed)
throw new IOException("This output stream has already been closed");
if (gzipstream != null) {
flushToGZip();
gzipstream.close();
gzipstream = null;
} else {
if (bufferCount > 0) {
if (debug > 2) {
System.out.print("output.write(");
System.out.write(buffer, 0, bufferCount);
System.out.println(")");
}
output.write(buffer, 0, bufferCount);
bufferCount = 0;
}
}
output.close();
closed = true;
}
/**
* 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) {
System.out.println("flush() @ CompressionResponseStream");
}
if (closed) {
throw new IOException("Cannot flush a closed output stream");
}
if (gzipstream != null) {
gzipstream.flush();
}
}
public void flushToGZip() throws IOException {
if (debug > 1) {
System.out.println("flushToGZip() @ CompressionResponseStream");
}
if (bufferCount > 0) {
if (debug > 1) {
System.out.println("flushing out to GZipStream, bufferCount = " + bufferCount);
}
writeToGZip(buffer, 0, bufferCount);
bufferCount = 0;
}
}
/**
* Write the specified byte to our output stream.
*
* @param b The byte to be written
*
* @exception IOException if an input/output error occurs
*/
@Override
public void write(int b) throws IOException {
if (debug > 1) {
System.out.println("write "+b+" in CompressionResponseStream ");
}
if (closed)
throw new IOException("Cannot write to a closed output stream");
if (bufferCount >= buffer.length) {
flushToGZip();
}
buffer[bufferCount++] = (byte) b;
}
/**
* Write <code>b.length</code> bytes from the specified byte array
* to our output stream.
*
* @param b The byte array to be written
*
* @exception IOException if an input/output error occurs
*/
@Override
public void write(byte b[]) throws IOException {
write(b, 0, b.length);
}
/**
* TODO SERVLET 3.1
*/
@Override
public boolean isReady() {
// TODO Auto-generated method stub
return false;
}
/**
* TODO SERVLET 3.1
*/
@Override
public void setWriteListener(WriteListener listener) {
// TODO Auto-generated method stub
}
/**
* Write <code>len</code> bytes from the specified byte array, starting
* at the specified offset, to our output stream.
*
* @param b The byte array containing the bytes to be written
* @param off Zero-relative starting offset of the bytes to be written
* @param len The number of bytes to be written
*
* @exception IOException if an input/output error occurs
*/
@Override
public void write(byte b[], int off, int len) throws IOException {
if (debug > 1) {
System.out.println("write, bufferCount = " + bufferCount + " len = " + len + " off = " + off);
}
if (debug > 2) {
System.out.print("write(");
System.out.write(b, off, len);
System.out.println(")");
}
if (closed)
throw new IOException("Cannot write to a closed output stream");
if (len == 0)
return;
// Can we write into buffer ?
if (len <= (buffer.length - bufferCount)) {
System.arraycopy(b, off, buffer, bufferCount, len);
bufferCount += len;
return;
}
// There is not enough space in buffer. Flush it ...
flushToGZip();
// ... and try again. Note, that bufferCount = 0 here !
if (len <= (buffer.length - bufferCount)) {
System.arraycopy(b, off, buffer, bufferCount, len);
bufferCount += len;
return;
}
// write direct to gzip
writeToGZip(b, off, len);
}
public void writeToGZip(byte b[], int off, int len) throws IOException {
if (debug > 1) {
System.out.println("writeToGZip, len = " + len);
}
if (debug > 2) {
System.out.print("writeToGZip(");
System.out.write(b, off, len);
System.out.println(")");
}
if (gzipstream == null) {
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);
}
}
gzipstream.write(b, off, len);
}
// -------------------------------------------------------- Package Methods
/**
* Has this response stream been closed?
*
* @return <code>true</code> if the stream has been closed, otherwise false.
*/
public boolean closed() {
return closed;
}
/**
* 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;
}
}

View File

@@ -0,0 +1,285 @@
/*
* 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 compressionFilters;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
/**
* Implementation of <b>HttpServletResponseWrapper</b> that works with
* the CompressionServletResponseStream implementation..
*
* @author Amy Roh
* @author Dmitri Valdin
*/
public class CompressionServletResponseWrapper
extends HttpServletResponseWrapper {
// ----------------------------------------------------- Constructor
/**
* Calls the parent constructor which creates a ServletResponse adaptor
* wrapping the given response object.
*
* @param response The response object to be wrapped.
*/
public CompressionServletResponseWrapper(HttpServletResponse response) {
super(response);
origResponse = response;
if (debug > 1) {
System.out.println("CompressionServletResponseWrapper constructor gets called");
}
}
// ----------------------------------------------------- Instance Variables
/**
* Original response
*/
protected final HttpServletResponse origResponse;
/**
* The ServletOutputStream that has been returned by
* <code>getOutputStream()</code>, if any.
*/
protected ServletOutputStream stream = null;
/**
* The PrintWriter that has been returned by
* <code>getWriter()</code>, if any.
*/
protected PrintWriter writer = null;
/**
* The threshold number to compress
*/
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
*/
protected int debug = 0;
/**
* keeps a copy of all headers set
*/
private final Map<String,String> headerCopies = new HashMap<>();
// --------------------------------------------------------- Public Methods
/**
* Set threshold number.
*
* @param threshold The new compression threshold
*/
public void setCompressionThreshold(int threshold) {
if (debug > 1) {
System.out.println("setCompressionThreshold to " + threshold);
}
this.compressionThreshold = threshold;
}
/**
* Set compression buffer.
*
* @param buffer New size of buffer to use for compressed output
*/
public void setCompressionBuffer(int buffer) {
if (debug > 1) {
System.out.println("setCompressionBuffer to " + buffer);
}
this.compressionBuffer = buffer;
}
/**
* Set compressible mime types.
*
* @param mimeTypes The new list of mime types that will be considered for
* compression
*/
public void setCompressionMimeTypes(String[] mimeTypes) {
if (debug > 1) {
System.out.println("setCompressionMimeTypes to " +
Arrays.toString(mimeTypes));
}
this.compressionMimeTypes = mimeTypes;
}
/**
* Set debug level.
*
* @param debug The new debug level
*/
public void setDebugLevel(int debug) {
this.debug = debug;
}
/**
* Create and return a ServletOutputStream to write the content
* associated with this Response.
*
* @exception IOException if an input/output error occurs
*
* @return A new servlet output stream that compressed any data written to
* it
*/
protected ServletOutputStream createOutputStream() throws IOException {
if (debug > 1) {
System.out.println("createOutputStream gets called");
}
CompressionResponseStream stream = new CompressionResponseStream(
this, origResponse.getOutputStream());
stream.setDebugLevel(debug);
stream.setCompressionThreshold(compressionThreshold);
stream.setCompressionBuffer(compressionBuffer);
stream.setCompressionMimeTypes(compressionMimeTypes);
return stream;
}
/**
* Finish a response.
*/
public void finishResponse() {
try {
if (writer != null) {
writer.close();
} else {
if (stream != null)
stream.close();
}
} catch (IOException e) {
// Ignore
}
}
// ------------------------------------------------ ServletResponse Methods
/**
* Flush the buffer and commit this response.
*
* @exception IOException if an input/output error occurs
*/
@Override
public void flushBuffer() throws IOException {
if (debug > 1) {
System.out.println("flush buffer @ GZipServletResponseWrapper");
}
((CompressionResponseStream)stream).flush();
}
/**
* Return the servlet output stream associated with this Response.
*
* @exception IllegalStateException if <code>getWriter</code> has
* already been called for this response
* @exception IOException if an input/output error occurs
*/
@Override
public ServletOutputStream getOutputStream() throws IOException {
if (writer != null)
throw new IllegalStateException("getWriter() has already been called for this response");
if (stream == null)
stream = createOutputStream();
if (debug > 1) {
System.out.println("stream is set to "+stream+" in getOutputStream");
}
return stream;
}
/**
* Return the writer associated with this Response.
*
* @exception IllegalStateException if <code>getOutputStream</code> has
* already been called for this response
* @exception IOException if an input/output error occurs
*/
@Override
public PrintWriter getWriter() throws IOException {
if (writer != null)
return writer;
if (stream != null)
throw new IllegalStateException("getOutputStream() has already been called for this response");
stream = createOutputStream();
if (debug > 1) {
System.out.println("stream is set to "+stream+" in getWriter");
}
String charEnc = origResponse.getCharacterEncoding();
if (debug > 1) {
System.out.println("character encoding is " + charEnc);
}
writer = new PrintWriter(new OutputStreamWriter(stream, charEnc));
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);
}
@Override
public void setHeader(String name, String value) {
headerCopies.put(name, value);
super.setHeader(name, value);
}
}

View File

@@ -0,0 +1,153 @@
/*
* 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 dates;
import java.util.Calendar;
import java.util.Date;
public class JspCalendar {
final Calendar calendar;
public JspCalendar() {
calendar = Calendar.getInstance();
Date trialTime = new Date();
calendar.setTime(trialTime);
}
public int getYear() {
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];
}
public String getDay() {
int x = getDayOfWeek();
String[] days = new String[] {"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"};
if (x > 7)
return "Unknown to Man";
return days[x - 1];
}
public int getMonthInt() {
return 1 + calendar.get(Calendar.MONTH);
}
public String getDate() {
return getMonthInt() + "/" + getDayOfMonth() + "/" + getYear();
}
public String getTime() {
return getHour() + ":" + getMinute() + ":" + getSecond();
}
public int getDayOfMonth() {
return calendar.get(Calendar.DAY_OF_MONTH);
}
public int getDayOfYear() {
return calendar.get(Calendar.DAY_OF_YEAR);
}
public int getWeekOfYear() {
return calendar.get(Calendar.WEEK_OF_YEAR);
}
public int getWeekOfMonth() {
return calendar.get(Calendar.WEEK_OF_MONTH);
}
public int getDayOfWeek() {
return calendar.get(Calendar.DAY_OF_WEEK);
}
public int getHour() {
return calendar.get(Calendar.HOUR_OF_DAY);
}
public int getMinute() {
return calendar.get(Calendar.MINUTE);
}
public int getSecond() {
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());
}
private static void p(String x) {
System.out.println(x);
}
public int getEra() {
return calendar.get(Calendar.ERA);
}
public String getUSTimeZone() {
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);
}
public int getDSTOffset() {
return calendar.get(Calendar.DST_OFFSET)/(60*60*1000);
}
public int getAMPM() {
return calendar.get(Calendar.AM_PM);
}
}

View File

@@ -0,0 +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.
*/
package error;
public class Smart {
String name = "JSP";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@@ -0,0 +1,74 @@
/*
* 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 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;
}
@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;
}
@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 Tag parent;
}

View File

@@ -0,0 +1,87 @@
/*
* 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;
/**
* Example1: the simplest tag
* Collect attributes and call into some actions
*
* <foo att1="..." att2="...." att3="...." />
*/
public class FooTag extends ExampleTagBase {
private static final long serialVersionUID = 1L;
private final 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);
}
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;
}
@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;
}
pageContext.setAttribute("member", atts[i]);
i++;
return EVAL_BODY_BUFFERED;
} catch (IOException ex) {
throw new JspTagException(ex.toString());
}
}
}

View File

@@ -0,0 +1,36 @@
/*
* 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 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[]
{
new VariableInfo("member",
"String",
true,
VariableInfo.NESTED)
};
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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;
/**
* Log the contents of the body. Could be used to handle errors etc.
*/
public class LogTag extends ExampleTagBase {
private static final long serialVersionUID = 1L;
boolean toBrowser = false;
public void setToBrowser(String value) {
if (value == null)
toBrowser = false;
else if (value.equalsIgnoreCase("true"))
toBrowser = true;
else
toBrowser = false;
}
@Override
public int doStartTag() throws JspException {
return EVAL_BODY_BUFFERED;
}
@Override
public int doAfterBody() throws JspException {
try {
String s = bodyOut.getString();
System.err.println(s);
if (toBrowser)
bodyOut.writeOut(bodyOut.getEnclosingWriter());
return SKIP_BODY;
} catch (IOException ex) {
throw new JspTagException(ex.toString());
}
}
}

View File

@@ -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();
}
}

View File

@@ -0,0 +1,137 @@
/*
* 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;
/**
* Example filter that can be attached to either an individual servlet
* or to a URL pattern. This filter performs the following functions:
* <ul>
* <li>Attaches itself as a request attribute, under the attribute name
* defined by the value of the <code>attribute</code> initialization
* parameter.</li>
* <li>Calculates the number of milliseconds required to perform the
* servlet processing required by this request, including any
* subsequently defined filters, and logs the result to the servlet
* context log for this application.
* </ul>
*
* @author Craig McClanahan
*/
public final class ExampleFilter implements Filter {
// ----------------------------------------------------- Instance Variables
/**
* The request attribute name under which we store a reference to ourself.
*/
private String attribute = null;
/**
* 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.
*/
@Override
public void destroy() {
this.attribute = null;
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 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 {
// 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();
chain.doFilter(request, response);
long stopTime = System.currentTimeMillis();
filterConfig.getServletContext().log
(this.toString() + ": " + (stopTime - startTime) +
" milliseconds");
}
/**
* Place this filter into service.
*
* @param fConfig The filter configuration object
*/
@Override
public void init(FilterConfig fConfig) throws ServletException {
this.filterConfig = fConfig;
this.attribute = fConfig.getInitParameter("attribute");
}
/**
* Return a String representation of this object.
*/
@Override
public String toString() {
if (filterConfig == null)
return ("TimingFilter()");
StringBuilder sb = new StringBuilder("TimingFilter(");
sb.append(filterConfig);
sb.append(")");
return sb.toString();
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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;
public class BookBean {
private final String title;
private final String author;
private final String isbn;
public BookBean( String title, String author, String isbn ) {
this.title = title;
this.author = author;
this.isbn = isbn;
}
public String getTitle() {
return this.title;
}
public String getAuthor() {
return this.author;
}
public String getIsbn() {
return this.isbn;
}
}

View File

@@ -0,0 +1,36 @@
/*
* 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;
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;
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.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 StringBuilder( text ).reverse().toString();
}
public static int numVowels( String text ) {
String vowels = "aeiouAEIOU";
int result = 0;
for( int i = 0; i < text.length(); i++ ) {
if( vowels.indexOf( text.charAt( i ) ) != -1 ) {
result++;
}
}
return result;
}
public static String caps( String text ) {
return text.toUpperCase(Locale.ENGLISH);
}
}

View File

@@ -0,0 +1,58 @@
/*
* 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.simpletag;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.DynamicAttributes;
import javax.servlet.jsp.tagext.SimpleTagSupport;
/**
* SimpleTag handler that echoes all its attributes
*/
public class EchoAttributesTag
extends SimpleTagSupport
implements DynamicAttributes
{
private final List<String> keys = new ArrayList<>();
private final List<Object> values = new ArrayList<>();
@Override
public void doTag() throws JspException, IOException {
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>" );
}
}
@Override
public void setDynamicAttribute( String uri, String localName,
Object value )
throws JspException
{
keys.add( localName );
values.add( value );
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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.simpletag;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import jsp2.examples.BookBean;
/**
* SimpleTag handler that pretends to search for a book, and stores
* the result in a scoped variable.
*/
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. Tolkien";
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;
}
}

View File

@@ -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 jsp2.examples.simpletag;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
/**
* SimpleTag handler that prints "Hello, world!"
*/
public class HelloWorldSimpleTag extends SimpleTagSupport {
@Override
public void doTag() throws JspException, IOException {
getJspContext().getOut().write( "Hello, world!" );
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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.simpletag;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
/**
* 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);
}
}
public void setNum(int num) {
this.num = num;
}
}

View File

@@ -0,0 +1,87 @@
/*
* 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.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;
/**
* 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 final Random random = new Random();
private JspFragment fragment1;
private JspFragment fragment2;
private JspFragment fragment3;
@Override
public void doTag() throws JspException, IOException {
switch(random.nextInt(6)) {
case 0:
fragment1.invoke( null );
fragment2.invoke( null );
fragment3.invoke( null );
break;
case 1:
fragment1.invoke( null );
fragment3.invoke( null );
fragment2.invoke( null );
break;
case 2:
fragment2.invoke( null );
fragment1.invoke( null );
fragment3.invoke( null );
break;
case 3:
fragment2.invoke( null );
fragment3.invoke( null );
fragment1.invoke( null );
break;
case 4:
fragment3.invoke( null );
fragment1.invoke( null );
fragment2.invoke( null );
break;
case 5:
fragment3.invoke( null );
fragment2.invoke( null );
fragment1.invoke( null );
break;
}
}
public void setFragment1( JspFragment fragment1 ) {
this.fragment1 = fragment1;
}
public void setFragment2( JspFragment fragment2 ) {
this.fragment2 = fragment2;
}
public void setFragment3( JspFragment fragment3 ) {
this.fragment3 = fragment3;
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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.simpletag;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
/**
* Displays a tile as a single cell in a table.
*/
public class TileSimpleTag extends SimpleTagSupport {
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 +
"</center></font></td>" );
}
public void setColor( String color ) {
this.color = color;
}
public void setLabel( String label ) {
this.label = label;
}
}

View File

@@ -0,0 +1,138 @@
/*
* 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.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/**
* 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.
*
* @author Craig R. McClanahan
*/
public final class ContextListener
implements ServletContextAttributeListener, ServletContextListener {
// ----------------------------------------------------- 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 servlet context attribute event
*/
@Override
public void attributeAdded(ServletContextAttributeEvent event) {
log("attributeAdded('" + event.getName() + "', '" +
event.getValue() + "')");
}
/**
* Record the fact that a servlet context attribute was removed.
*
* @param event The servlet context attribute event
*/
@Override
public void attributeRemoved(ServletContextAttributeEvent event) {
log("attributeRemoved('" + event.getName() + "', '" +
event.getValue() + "')");
}
/**
* Record the fact that a servlet context attribute was replaced.
*
* @param event The servlet context attribute event
*/
@Override
public void attributeReplaced(ServletContextAttributeEvent event) {
log("attributeReplaced('" + event.getName() + "', '" +
event.getValue() + "')");
}
/**
* Record the fact that this web application has been destroyed.
*
* @param event The servlet context event
*/
@Override
public void contextDestroyed(ServletContextEvent event) {
log("contextDestroyed()");
this.context = null;
}
/**
* Record the fact that this web application has been initialized.
*
* @param event The servlet context event
*/
@Override
public void contextInitialized(ServletContextEvent event) {
this.context = event.getServletContext();
log("contextInitialized()");
}
// -------------------------------------------------------- Private Methods
/**
* Log a message to the servlet context application log.
*
* @param message Message to be logged
*/
private void log(String message) {
if (context != null)
context.log("ContextListener: " + message);
else
System.out.println("ContextListener: " + message);
}
}

View File

@@ -0,0 +1,160 @@
/*
* 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;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
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.
*
* @author Craig R. McClanahan
*/
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
*/
@Override
public void attributeAdded(HttpSessionBindingEvent event) {
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
*/
@Override
public void attributeRemoved(HttpSessionBindingEvent event) {
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
*/
@Override
public void attributeReplaced(HttpSessionBindingEvent event) {
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
*/
@Override
public void contextDestroyed(ServletContextEvent event) {
log("contextDestroyed()");
this.context = null;
}
/**
* Record the fact that this web application has been initialized.
*
* @param event
* The servlet context event
*/
@Override
public void contextInitialized(ServletContextEvent event) {
this.context = event.getServletContext();
log("contextInitialized()");
}
/**
* Record the fact that a session has been created.
*
* @param event
* The session event
*/
@Override
public void sessionCreated(HttpSessionEvent event) {
log("sessionCreated('" + event.getSession().getId() + "')");
}
/**
* Record the fact that a session has been destroyed.
*
* @param event
* The session event
*/
@Override
public void sessionDestroyed(HttpSessionEvent event) {
log("sessionDestroyed('" + event.getSession().getId() + "')");
}
// -------------------------------------------------------- Private Methods
/**
* Log a message to the servlet context application log.
*
* @param message
* Message to be logged
*/
private void log(String message) {
if (context != null)
context.log("SessionListener: " + message);
else
System.out.println("SessionListener: " + message);
}
}

View File

@@ -0,0 +1,142 @@
/*
* 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 nonblocking;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import javax.servlet.AsyncContext;
import javax.servlet.ReadListener;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* This doesn't do anything particularly useful - it just counts the total
* number of bytes in a request body while demonstrating how to perform
* non-blocking reads.
*/
public class ByteCounter extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
resp.getWriter().println("Try again using a POST request.");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
// Non-blocking IO requires async
AsyncContext ac = req.startAsync();
// Use a single listener for read and write. Listeners often need to
// share state to coordinate reads and writes and this is much easier as
// a single object.
@SuppressWarnings("unused")
CounterListener listener = new CounterListener(
ac, req.getInputStream(), resp.getOutputStream());
}
/**
* Keep in mind that each call may well be on a different thread to the
* previous call. Ensure that changes in values will be visible across
* threads. There should only ever be one container thread at a time calling
* the listener.
*/
private static class CounterListener implements ReadListener, WriteListener {
private final AsyncContext ac;
private final ServletInputStream sis;
private final ServletOutputStream sos;
private volatile boolean readFinished = false;
private volatile long totalBytesRead = 0;
private byte[] buffer = new byte[8192];
private CounterListener(AsyncContext ac, ServletInputStream sis,
ServletOutputStream sos) {
this.ac = ac;
this.sis = sis;
this.sos = sos;
// In Tomcat, the order the listeners are set controls the order
// that the first calls are made. In this case, the read listener
// will be called before the write listener.
sis.setReadListener(this);
sos.setWriteListener(this);
}
@Override
public void onDataAvailable() throws IOException {
int read = 0;
// Loop as long as there is data to read. If isReady() returns false
// the socket will be added to the poller and onDataAvailable() will
// be called again as soon as there is more data to read.
while (sis.isReady() && read > -1) {
read = sis.read(buffer);
if (read > 0) {
totalBytesRead += read;
}
}
}
@Override
public void onAllDataRead() throws IOException {
readFinished = true;
// If sos is not ready to write data, the call to isReady() will
// register the socket with the poller which will trigger a call to
// onWritePossible() when the socket is ready to have data written
// to it.
if (sos.isReady()) {
onWritePossible();
}
}
@Override
public void onWritePossible() throws IOException {
if (readFinished) {
// Must be ready to write data if onWritePossible was called
String msg = "Total bytes written = [" + totalBytesRead + "]";
sos.write(msg.getBytes(StandardCharsets.UTF_8));
ac.complete();
}
}
@Override
public void onError(Throwable throwable) {
// Should probably log the throwable
ac.complete();
}
}
}

View File

@@ -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 nonblocking;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.AsyncContext;
import javax.servlet.ReadListener;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* This doesn't do anything particularly useful - it just writes a series of
* numbers to the response body while demonstrating how to perform non-blocking
* writes.
*/
public class NumberWriter extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
// Non-blocking IO requires async
AsyncContext ac = req.startAsync();
// Use a single listener for read and write. Listeners often need to
// share state to coordinate reads and writes and this is much easier as
// a single object.
@SuppressWarnings("unused")
NumberWriterListener listener = new NumberWriterListener(
ac, req.getInputStream(), resp.getOutputStream());
}
/**
* Keep in mind that each call may well be on a different thread to the
* previous call. Ensure that changes in values will be visible across
* threads. There should only ever be one container thread at a time calling
* the listener.
*/
private static class NumberWriterListener implements ReadListener,
WriteListener {
private static final int LIMIT = 10000;
private final AsyncContext ac;
private final ServletInputStream sis;
private final ServletOutputStream sos;
private final AtomicInteger counter = new AtomicInteger(0);
private volatile boolean readFinished = false;
private byte[] buffer = new byte[8192];
private NumberWriterListener(AsyncContext ac, ServletInputStream sis,
ServletOutputStream sos) {
this.ac = ac;
this.sis = sis;
this.sos = sos;
// In Tomcat, the order the listeners are set controls the order
// that the first calls are made. In this case, the read listener
// will be called before the write listener.
sis.setReadListener(this);
sos.setWriteListener(this);
}
@Override
public void onDataAvailable() throws IOException {
// There should be no data to read
int read = 0;
// Loop as long as there is data to read. If isReady() returns false
// the socket will be added to the poller and onDataAvailable() will
// be called again as soon as there is more data to read.
while (sis.isReady() && read > -1) {
read = sis.read(buffer);
if (read > 0) {
throw new IOException("Data was present in input stream");
}
}
}
@Override
public void onAllDataRead() throws IOException {
readFinished = true;
// If sos is not ready to write data, the call to isReady() will
// register the socket with the poller which will trigger a call to
// onWritePossible() when the socket is ready to have data written
// to it.
if (sos.isReady()) {
onWritePossible();
}
}
@Override
public void onWritePossible() throws IOException {
if (readFinished) {
int i = counter.get();
boolean ready = true;
while (i < LIMIT && ready) {
i = counter.incrementAndGet();
String msg = String.format("%1$020d\n", Integer.valueOf(i));
sos.write(msg.getBytes(StandardCharsets.UTF_8));
ready = sos.isReady();
}
if (i == LIMIT) {
ac.complete();
}
}
}
@Override
public void onError(Throwable throwable) {
// Should probably log the throwable
ac.complete();
}
}
}

View File

@@ -0,0 +1,99 @@
/*
* 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.
*/
package num;
import java.io.Serializable;
import java.util.Random;
public class NumberGuessBean implements Serializable {
private static final long serialVersionUID = 1L;
private int answer;
private String hint;
private int numGuesses;
private boolean success;
private final Random random = new Random();
public NumberGuessBean() {
reset();
}
public int getAnswer() {
return answer;
}
public void setAnswer(int answer) {
this.answer = answer;
}
public String getHint() {
return "" + hint;
}
public void setHint(String hint) {
this.hint = hint;
}
public void setNumGuesses(int numGuesses) {
this.numGuesses = numGuesses;
}
public int getNumGuesses() {
return numGuesses;
}
public boolean getSuccess() {
return success;
}
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;
}
}

View File

@@ -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 sessions;
import java.util.Vector;
public class DummyCart {
final Vector<String> v = new Vector<>();
String submit = null;
String item = null;
private void addItem(String name) {
v.addElement(name);
}
private void removeItem(String name) {
v.removeElement(name);
}
public void setItem(String name) {
item = name;
}
public void setSubmit(String s) {
submit = s;
}
public String[] getItems() {
String[] s = new String[v.size()];
v.copyInto(s);
return s;
}
public void processRequest() {
// 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"))
removeItem(item);
// reset at the end of the request
reset();
}
// reset
private void reset() {
submit = null;
item = null;
}
}

View File

@@ -0,0 +1,85 @@
/*
* 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 cookieHeader, String sessionId) {
StringBuilder sb = new StringBuilder(cookieHeader.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(cookieHeader, ";");
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());
return name + "=" + filter(name, value, sessionId);
}
public static String filter(String cookieName, String cookieValue, String sessionId) {
if (cookieName.toLowerCase(Locale.ENGLISH).contains("jsessionid") &&
(sessionId == null || !cookieValue.contains(sessionId))) {
cookieValue = OBFUSCATED;
}
return cookieValue;
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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;
/**
* HTML filter utility.
*
* @author Craig R. McClanahan
* @author Tim Tye
*/
public final class HTMLFilter {
/**
* Filter the specified message string for characters that are sensitive
* in HTML. This avoids potential attacks caused by including JavaScript
* codes in the request URL that is often reported in error messages.
*
* @param message The message string to be filtered
*
* @return the filtered version of the message
*/
public static String filter(String message) {
if (message == null)
return null;
char content[] = new char[message.length()];
message.getChars(0, message.length(), content, 0);
StringBuilder result = new StringBuilder(content.length + 50);
for (int i = 0; i < content.length; i++) {
switch (content[i]) {
case '<':
result.append("&lt;");
break;
case '>':
result.append("&gt;");
break;
case '&':
result.append("&amp;");
break;
case '"':
result.append("&quot;");
break;
default:
result.append(content[i]);
}
}
return result.toString();
}
}

View File

@@ -0,0 +1,84 @@
/*
* 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 validators;
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;
/**
* Example tag library validator that simply dumps the XML version of each
* page to standard output (which will typically be sent to the file
* <code>$CATALINA_HOME/logs/catalina.out</code>). To utilize it, simply
* include a <code>taglib</code> directive for this tag library at the top
* of your JSP page.
*
* @author Craig McClanahan
*/
public class DebugValidator extends TagLibraryValidator {
// ----------------------------------------------------- Instance Variables
// --------------------------------------------------------- Public Methods
/**
* Validate a JSP page. This will get invoked once per directive in the
* JSP page. This method will return <code>null</code> if the page is
* valid; otherwise the method should return an array of
* <code>ValidationMessage</code> objects. An array of length zero is
* also interpreted as no errors.
*
* @param prefix The value of the prefix argument in this directive
* @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) {
System.out.println("---------- Prefix=" + prefix + " URI=" + uri +
"----------");
InputStream is = page.getInputStream();
while (true) {
try {
int ch = is.read();
if (ch < 0)
break;
System.out.print((char) ch);
} catch (IOException e) {
break;
}
}
System.out.println();
System.out.println("-----------------------------------------------");
return null;
}
}

View File

@@ -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<>();
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<>();
for (Class<?> clazz : scanned) {
if (clazz.getPackage().getName().startsWith("websocket.")) {
results.add(clazz);
}
}
return results;
}
}

View File

@@ -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<>();
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);
}
}
}
}

View File

@@ -0,0 +1,230 @@
/*
* 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 which are buffered until the previous
* send operation has finished.
*/
private final LinkedList<AbstractWebsocketMessage> messagesToSend =
new LinkedList<>();
/**
* 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 The message to send
*/
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|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 occurred).
// 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;
}
}
}
};
}

View File

@@ -0,0 +1,250 @@
/*
* 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;
/**
* The type.
*
* @return 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;
}
public DrawMessage(int type, byte colorR, byte colorG, byte colorB,
byte colorA, double thickness, double x1, double x2, double y1,
double y2) {
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;
}
/**
* Draws this DrawMessage onto the given Graphics2D.
*
* @param g The target for the DrawMessage
*/
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;
}
public static DrawMessage parseFromString(String str)
throws ParseException {
int type;
byte[] colors = new byte[4];
double thickness;
double[] coords = new double[4];
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]);
}
} 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]);
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);
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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 (ParseException e) {
// Client sent invalid data
// Ignore, TODO: maybe close connection
} catch (RuntimeException e) {
// Client sent invalid data.
// Ignore, TODO: maybe close connection
if (dontSwallowException) {
throw e;
}
}
} catch (RuntimeException ex) {
log.error("Unexpected exception: " + ex.toString(), ex);
}
}
});
}
};
}

View File

@@ -0,0 +1,496 @@
/*
* 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.Objects;
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 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<>();
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
*
* @return The newly created player
*/
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 The task to be executed
*/
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<>();
}
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 static 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<>();
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 The draw message received
* @param msgId The ID for the draw message received
*/
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) {
Objects.requireNonNull(content);
Objects.requireNonNull(type);
String completeMsg = String.valueOf(type.flag) + content;
client.sendMessage(new StringWebsocketMessage(completeMsg));
}
}
}

View File

@@ -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 {
}

View File

@@ -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;
}
}

View File

@@ -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 {
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,75 @@
/*
* 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;
/**
* The three annotated echo endpoints can be used to test with Autobahn and
* the following command "wstest -m fuzzingclient -s servers.json". See the
* Autobahn documentation for setup and general information.
*/
@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
}
}

View File

@@ -0,0 +1,128 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package websocket.echo;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.websocket.OnMessage;
import javax.websocket.PongMessage;
import javax.websocket.Session;
/**
* The three annotated echo endpoints can be used to test with Autobahn and
* the following command "wstest -m fuzzingclient -s servers.json". See the
* Autobahn documentation for setup and general information.
*
* Note: This one is disabled by default since it allocates memory, and needs
* to be enabled back.
*/
//@javax.websocket.server.ServerEndpoint("/websocket/echoAsyncAnnotation")
public class EchoAsyncAnnotation {
private static final Future<Void> COMPLETED = new CompletedFuture();
Future<Void> f = COMPLETED;
StringBuilder sb = null;
ByteArrayOutputStream bytes = null;
@OnMessage
public void echoTextMessage(Session session, String msg, boolean last) {
if (sb == null) {
sb = new StringBuilder();
}
sb.append(msg);
if (last) {
// Before we send the next message, have to wait for the previous
// message to complete
try {
f.get();
} catch (InterruptedException | ExecutionException e) {
// Let the container deal with it
throw new RuntimeException(e);
}
f = session.getAsyncRemote().sendText(sb.toString());
sb = null;
}
}
@OnMessage
public void echoBinaryMessage(byte[] msg, Session session, boolean last)
throws IOException {
if (bytes == null) {
bytes = new ByteArrayOutputStream();
}
bytes.write(msg);
if (last) {
// Before we send the next message, have to wait for the previous
// message to complete
try {
f.get();
} catch (InterruptedException | ExecutionException e) {
// Let the container deal with it
throw new RuntimeException(e);
}
f = session.getAsyncRemote().sendBinary(ByteBuffer.wrap(bytes.toByteArray()));
bytes = null;
}
}
/**
* Process a received pong. This is a NO-OP.
*
* @param pm Ignored.
*/
@OnMessage
public void echoPongMessage(PongMessage pm) {
// NO-OP
}
private static class CompletedFuture implements Future<Void> {
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return false;
}
@Override
public boolean isCancelled() {
return false;
}
@Override
public boolean isDone() {
return true;
}
@Override
public Void get() throws InterruptedException, ExecutionException {
return null;
}
@Override
public Void get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException,
TimeoutException {
return null;
}
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -0,0 +1,75 @@
/*
* 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.io.OutputStream;
import java.io.Writer;
import javax.websocket.OnMessage;
import javax.websocket.PongMessage;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
/**
* The three annotated echo endpoints can be used to test with Autobahn and
* the following command "wstest -m fuzzingclient -s servers.json". See the
* Autobahn documentation for setup and general information.
*/
@ServerEndpoint("/websocket/echoStreamAnnotation")
public class EchoStreamAnnotation {
Writer writer;
OutputStream stream;
@OnMessage
public void echoTextMessage(Session session, String msg, boolean last)
throws IOException {
if (writer == null) {
writer = session.getBasicRemote().getSendWriter();
}
writer.write(msg);
if (last) {
writer.close();
writer = null;
}
}
@OnMessage
public void echoBinaryMessage(byte[] msg, Session session, boolean last)
throws IOException {
if (stream == null) {
stream = session.getBasicRemote().getSendStream();
}
stream.write(msg);
stream.flush();
if (last) {
stream.close();
stream = null;
}
}
/**
* Process a received pong. This is a NO-OP.
*
* @param pm Ignored.
*/
@OnMessage
public void echoPongMessage(PongMessage pm) {
// NO-OP
}
}

View File

@@ -0,0 +1,20 @@
{
"options": {"failByDrop": false},
"outdir": "./reports/servers",
"servers": [
{"agent": "Basic",
"url": "ws://localhost:8080/examples/websocket/echoAnnotation",
"options": {"version": 18}},
{"agent": "Stream",
"url": "ws://localhost:8080/examples/websocket/echoStreamAnnotation",
"options": {"version": 18}},
{"agent": "Async",
"url": "ws://localhost:8080/examples/websocket/echoAsyncAnnotation",
"options": {"version": 18}}
],
"cases": ["*"],
"exclude-cases": [],
"exclude-agent-cases": {}
}

View File

@@ -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
}

View File

@@ -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;
}
}

View File

@@ -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<>();
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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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<>();
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();
}
}
}

View File

@@ -0,0 +1,230 @@
/*
* 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.
*/
import java.applet.Applet;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
/**
* Time!
*
* @author Rachel Gollub
*/
public class Clock2 extends Applet implements Runnable {
private static final long serialVersionUID = 1L;
Thread timer; // The thread that displays clock
int lastxs, lastys, lastxm,
lastym, lastxh, lastyh; // Dimensions used to draw hands
SimpleDateFormat formatter; // Formats the date displayed
String lastdate; // String to hold date displayed
Font clockFaceFont; // Font for number display on clock
Date currentDate; // Used to get date to display
Color handColor; // Color of main hands and dial
Color numberColor; // Color of second hand and numbers
@Override
public void init() {
lastxs = lastys = lastxm = lastym = lastxh = lastyh = 0;
formatter = new SimpleDateFormat ("EEE MMM dd hh:mm:ss yyyy", Locale.getDefault());
currentDate = new Date();
lastdate = formatter.format(currentDate);
clockFaceFont = new Font("Serif", Font.PLAIN, 14);
handColor = Color.blue;
numberColor = Color.darkGray;
try {
setBackground(new Color(Integer.parseInt(getParameter("bgcolor"),16)));
} catch (Exception e) {
// Ignore
}
try {
handColor = new Color(Integer.parseInt(getParameter("fgcolor1"),16));
} catch (Exception e) {
// Ignore
}
try {
numberColor = new Color(Integer.parseInt(getParameter("fgcolor2"),16));
} catch (Exception e) {
// Ignore
}
resize(300,300); // Set clock window size
}
// Plotpoints allows calculation to only cover 45 degrees of the circle,
// and then mirror
public void plotpoints(int x0, int y0, int x, int y, Graphics g) {
g.drawLine(x0+x,y0+y,x0+x,y0+y);
g.drawLine(x0+y,y0+x,x0+y,y0+x);
g.drawLine(x0+y,y0-x,x0+y,y0-x);
g.drawLine(x0+x,y0-y,x0+x,y0-y);
g.drawLine(x0-x,y0-y,x0-x,y0-y);
g.drawLine(x0-y,y0-x,x0-y,y0-x);
g.drawLine(x0-y,y0+x,x0-y,y0+x);
g.drawLine(x0-x,y0+y,x0-x,y0+y);
}
// Circle is just Bresenham's algorithm for a scan converted circle
public void circle(int x0, int y0, int r, Graphics g) {
int x,y;
float d;
x=0;
y=r;
d=5/4-r;
plotpoints(x0,y0,x,y,g);
while (y>x){
if (d<0) {
d=d+2*x+3;
x++;
}
else {
d=d+2*(x-y)+5;
x++;
y--;
}
plotpoints(x0,y0,x,y,g);
}
}
// Paint is the main part of the program
@Override
public void paint(Graphics g) {
int xh, yh, xm, ym, xs, ys, s = 0, m = 10, h = 10, xcenter, ycenter;
String today;
currentDate = new Date();
SimpleDateFormat formatter = new SimpleDateFormat("s",Locale.getDefault());
try {
s = Integer.parseInt(formatter.format(currentDate));
} catch (NumberFormatException n) {
s = 0;
}
formatter.applyPattern("m");
try {
m = Integer.parseInt(formatter.format(currentDate));
} catch (NumberFormatException n) {
m = 10;
}
formatter.applyPattern("h");
try {
h = Integer.parseInt(formatter.format(currentDate));
} catch (NumberFormatException n) {
h = 10;
}
formatter.applyPattern("EEE MMM dd HH:mm:ss yyyy");
today = formatter.format(currentDate);
xcenter=80;
ycenter=55;
// a= s* pi/2 - pi/2 (to switch 0,0 from 3:00 to 12:00)
// x = r(cos a) + xcenter, y = r(sin a) + ycenter
xs = (int)(Math.cos(s * Math.PI/30 - Math.PI/2) * 45 + xcenter);
ys = (int)(Math.sin(s * Math.PI/30 - Math.PI/2) * 45 + ycenter);
xm = (int)(Math.cos(m * Math.PI/30 - Math.PI/2) * 40 + xcenter);
ym = (int)(Math.sin(m * Math.PI/30 - Math.PI/2) * 40 + ycenter);
xh = (int)(Math.cos((h*30 + m/2) * Math.PI/180 - Math.PI/2) * 30 + xcenter);
yh = (int)(Math.sin((h*30 + m/2) * Math.PI/180 - Math.PI/2) * 30 + ycenter);
// Draw the circle and numbers
g.setFont(clockFaceFont);
g.setColor(handColor);
circle(xcenter,ycenter,50,g);
g.setColor(numberColor);
g.drawString("9",xcenter-45,ycenter+3);
g.drawString("3",xcenter+40,ycenter+3);
g.drawString("12",xcenter-5,ycenter-37);
g.drawString("6",xcenter-3,ycenter+45);
// Erase if necessary, and redraw
g.setColor(getBackground());
if (xs != lastxs || ys != lastys) {
g.drawLine(xcenter, ycenter, lastxs, lastys);
g.drawString(lastdate, 5, 125);
}
if (xm != lastxm || ym != lastym) {
g.drawLine(xcenter, ycenter-1, lastxm, lastym);
g.drawLine(xcenter-1, ycenter, lastxm, lastym); }
if (xh != lastxh || yh != lastyh) {
g.drawLine(xcenter, ycenter-1, lastxh, lastyh);
g.drawLine(xcenter-1, ycenter, lastxh, lastyh); }
g.setColor(numberColor);
g.drawString("", 5, 125);
g.drawString(today, 5, 125);
g.drawLine(xcenter, ycenter, xs, ys);
g.setColor(handColor);
g.drawLine(xcenter, ycenter-1, xm, ym);
g.drawLine(xcenter-1, ycenter, xm, ym);
g.drawLine(xcenter, ycenter-1, xh, yh);
g.drawLine(xcenter-1, ycenter, xh, yh);
lastxs=xs; lastys=ys;
lastxm=xm; lastym=ym;
lastxh=xh; lastyh=yh;
lastdate = today;
currentDate=null;
}
@Override
public void start() {
timer = new Thread(this);
timer.start();
}
@Override
public void stop() {
timer = null;
}
@Override
public void run() {
Thread me = Thread.currentThread();
while (timer == me) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
repaint();
}
}
@Override
public void update(Graphics g) {
paint(g);
}
@Override
public String getAppletInfo() {
return "Title: A Clock \nAuthor: Rachel Gollub, 1995 \nAn analog clock.";
}
@Override
public String[][] getParameterInfo() {
String[][] info = {
{"bgcolor", "hexadecimal RGB number", "The background color. Default is the color of your browser."},
{"fgcolor1", "hexadecimal RGB number", "The color of the hands and dial. Default is blue."},
{"fgcolor2", "hexadecimal RGB number", "The color of the seconds hand and numbers. Default is dark gray."}
};
return info;
}
}

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!--
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.
-->
<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://java.sun.com/j2ee/dtd/web-jsptaglibrary_1_2.dtd">
<!-- a tag library descriptor -->
<taglib>
<tlib-version>1.0</tlib-version>
<jsp-version>1.2</jsp-version>
<short-name>debug</short-name>
<uri>http://tomcat.apache.org/debug-taglib</uri>
<description>
This tag library defines no tags. Instead, its purpose is encapsulated
in the TagLibraryValidator implementation that simply outputs the XML
version of a JSP page to standard output, whenever this tag library is
referenced in a "taglib" directive in a JSP page.
</description>
<validator>
<validator-class>validators.DebugValidator</validator-class>
</validator>
<!-- This is a dummy tag solely to satisfy DTD requirements -->
<tag>
<name>log</name>
<tag-class>examples.LogTag</tag-class>
<body-content>TAGDEPENDENT</body-content>
<description>
Perform a server side action; Log the message.
</description>
<attribute>
<name>toBrowser</name>
<required>false</required>
</attribute>
</tag>
</taglib>

View File

@@ -0,0 +1,107 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!--
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.
-->
<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://java.sun.com/j2ee/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
<tlib-version>1.0</tlib-version>
<jsp-version>1.2</jsp-version>
<short-name>simple</short-name>
<uri>http://tomcat.apache.org/example-taglib</uri>
<description>
A simple tab library for the examples
</description>
<!-- A simple Tag -->
<!-- foo tag -->
<tag>
<name>foo</name>
<tag-class>examples.FooTag</tag-class>
<tei-class>examples.FooTagExtraInfo</tei-class>
<body-content>JSP</body-content>
<description>
Perform a server side action; uses 3 mandatory attributes
</description>
<attribute>
<name>att1</name>
<required>true</required>
</attribute>
<attribute>
<name>att2</name>
<required>true</required>
</attribute>
<attribute>
<name>att3</name>
<required>true</required>
</attribute>
</tag>
<!-- Another simple tag -->
<!-- log tag -->
<tag>
<name>log</name>
<tag-class>examples.LogTag</tag-class>
<body-content>TAGDEPENDENT</body-content>
<description>
Perform a server side action; Log the message.
</description>
<attribute>
<name>toBrowser</name>
<required>false</required>
</attribute>
</tag>
<!-- Another simple Tag -->
<!-- values tag -->
<tag>
<name>values</name>
<tag-class>examples.ValuesTag</tag-class>
<body-content>empty</body-content>
<description>
Accept and return values of different types. This tag is used
to illustrate type coercions.
</description>
<attribute>
<name>object</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.Object</type>
</attribute>
<attribute>
<name>string</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.String</type>
</attribute>
<attribute>
<name>long</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
<type>long</type>
</attribute>
<attribute>
<name>double</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
<type>double</type>
</attribute>
</tag>
</taglib>

View File

@@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<description>A tag library exercising SimpleTag handlers.</description>
<tlib-version>1.0</tlib-version>
<short-name>SimpleTagLibrary</short-name>
<uri>http://tomcat.apache.org/jsp2-example-taglib</uri>
<tag>
<description>Outputs Hello, World</description>
<name>helloWorld</name>
<tag-class>jsp2.examples.simpletag.HelloWorldSimpleTag</tag-class>
<body-content>empty</body-content>
</tag>
<tag>
<description>Repeats the body of the tag 'num' times</description>
<name>repeat</name>
<tag-class>jsp2.examples.simpletag.RepeatSimpleTag</tag-class>
<body-content>scriptless</body-content>
<variable>
<description>Current invocation count (1 to num)</description>
<name-given>count</name-given>
</variable>
<attribute>
<name>num</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag>
<description>Populates the page context with a BookBean</description>
<name>findBook</name>
<tag-class>jsp2.examples.simpletag.FindBookSimpleTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>var</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag>
<description>
Takes 3 fragments and invokes them in a random order
</description>
<name>shuffle</name>
<tag-class>jsp2.examples.simpletag.ShuffleSimpleTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>fragment1</name>
<required>true</required>
<fragment>true</fragment>
</attribute>
<attribute>
<name>fragment2</name>
<required>true</required>
<fragment>true</fragment>
</attribute>
<attribute>
<name>fragment3</name>
<required>true</required>
<fragment>true</fragment>
</attribute>
</tag>
<tag>
<description>Outputs a colored tile</description>
<name>tile</name>
<tag-class>jsp2.examples.simpletag.TileSimpleTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>color</name>
<required>true</required>
</attribute>
<attribute>
<name>label</name>
<required>true</required>
</attribute>
</tag>
<tag>
<description>
Tag that echoes all its attributes and body content
</description>
<name>echoAttributes</name>
<tag-class>jsp2.examples.simpletag.EchoAttributesTag</tag-class>
<body-content>empty</body-content>
<dynamic-attributes>true</dynamic-attributes>
</tag>
<function>
<description>Reverses the characters in the given String</description>
<name>reverse</name>
<function-class>jsp2.examples.el.Functions</function-class>
<function-signature>java.lang.String reverse( java.lang.String )</function-signature>
</function>
<function>
<description>Counts the number of vowels (a,e,i,o,u) in the given String</description>
<name>countVowels</name>
<function-class>jsp2.examples.el.Functions</function-class>
<function-signature>java.lang.String numVowels( java.lang.String )</function-signature>
</function>
<function>
<description>Converts the string to all caps</description>
<name>caps</name>
<function-class>jsp2.examples.el.Functions</function-class>
<function-signature>java.lang.String caps( java.lang.String )</function-signature>
</function>
</taglib>

View File

@@ -0,0 +1,55 @@
<!--
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.
-->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ attribute name="normalPrice" fragment="true" %>
<%@ attribute name="onSale" fragment="true" %>
<%@ variable name-given="name" %>
<%@ variable name-given="price" %>
<%@ variable name-given="origPrice" %>
<%@ variable name-given="salePrice" %>
<table border="1">
<tr>
<td>
<c:set var="name" value="Hand-held Color PDA"/>
<c:set var="price" value="$298.86"/>
<jsp:invoke fragment="normalPrice"/>
</td>
<td>
<c:set var="name" value="4-Pack 150 Watt Light Bulbs"/>
<c:set var="origPrice" value="$2.98"/>
<c:set var="salePrice" value="$2.32"/>
<jsp:invoke fragment="onSale"/>
</td>
<td>
<c:set var="name" value="Digital Cellular Phone"/>
<c:set var="price" value="$68.74"/>
<jsp:invoke fragment="normalPrice"/>
</td>
<td>
<c:set var="name" value="Baby Grand Piano"/>
<c:set var="price" value="$10,800.00"/>
<jsp:invoke fragment="normalPrice"/>
</td>
<td>
<c:set var="name" value="Luxury Car w/ Leather Seats"/>
<c:set var="origPrice" value="$23,980.00"/>
<c:set var="salePrice" value="$21,070.00"/>
<jsp:invoke fragment="onSale"/>
</td>
</tr>
</table>

View File

@@ -0,0 +1,17 @@
<!--
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.
-->
Hello, world!

View File

@@ -0,0 +1,29 @@
<!--
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.
-->
<%@ attribute name="color" %>
<%@ attribute name="bgcolor" %>
<%@ attribute name="title" %>
<table border="1" bgcolor="${color}">
<tr>
<td><b>${title}</b></td>
</tr>
<tr>
<td bgcolor="${bgcolor}">
<jsp:doBody/>
</td>
</tr>
</table>

View File

@@ -0,0 +1,399 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1"
metadata-complete="true">
<description>
Servlet and JSP Examples.
</description>
<display-name>Servlet and JSP Examples</display-name>
<!-- Define example filters -->
<filter>
<filter-name>Timing Filter</filter-name>
<filter-class>filters.ExampleFilter</filter-class>
<init-param>
<param-name>attribute</param-name>
<param-value>filters.ExampleFilter</param-value>
</init-param>
</filter>
<filter>
<filter-name>Request Dumper Filter</filter-name>
<filter-class>org.apache.catalina.filters.RequestDumperFilter</filter-class>
</filter>
<!-- Example filter to set character encoding on each request -->
<filter>
<filter-name>Set Character Encoding</filter-name>
<filter-class>org.apache.catalina.filters.SetCharacterEncodingFilter</filter-class>
<async-supported>true</async-supported>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>ignore</param-name>
<param-value>false</param-value>
</init-param>
</filter>
<filter>
<filter-name>Compression Filter</filter-name>
<filter-class>compressionFilters.CompressionFilter</filter-class>
<init-param>
<param-name>compressionThreshold</param-name>
<param-value>128</param-value>
</init-param>
<init-param>
<param-name>compressionBuffer</param-name>
<param-value>8192</param-value>
</init-param>
<init-param>
<param-name>compressionMimeTypes</param-name>
<param-value>text/html,text/plain,text/xml</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
</filter>
<!-- Define filter mappings for the timing filters -->
<!--
<filter-mapping>
<filter-name>Timing Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
-->
<!-- Example filter mapping to apply the "Set Character Encoding" filter
to *all* requests processed by this web application -->
<filter-mapping>
<filter-name>Set Character Encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--
<filter-mapping>
<filter-name>Compression Filter</filter-name>
<url-pattern>/CompressionTest</url-pattern>
</filter-mapping>
-->
<!--
<filter-mapping>
<filter-name>Request Dumper Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
-->
<!-- Define example application events listeners -->
<listener>
<listener-class>listeners.ContextListener</listener-class>
</listener>
<listener>
<listener-class>listeners.SessionListener</listener-class>
</listener>
<!-- Define listeners required by examples -->
<listener>
<listener-class>async.AsyncStockContextListener</listener-class>
</listener>
<!-- Define servlets that are included in the example application -->
<servlet>
<servlet-name>ServletToJsp</servlet-name>
<servlet-class>ServletToJsp</servlet-class>
</servlet>
<servlet>
<servlet-name>CompressionFilterTestServlet</servlet-name>
<servlet-class>compressionFilters.CompressionFilterTestServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>HelloWorldExample</servlet-name>
<servlet-class>HelloWorldExample</servlet-class>
</servlet>
<servlet>
<servlet-name>RequestInfoExample</servlet-name>
<servlet-class>RequestInfoExample</servlet-class>
</servlet>
<servlet>
<servlet-name>RequestHeaderExample</servlet-name>
<servlet-class>RequestHeaderExample</servlet-class>
</servlet>
<servlet>
<servlet-name>RequestParamExample</servlet-name>
<servlet-class>RequestParamExample</servlet-class>
</servlet>
<servlet>
<servlet-name>CookieExample</servlet-name>
<servlet-class>CookieExample</servlet-class>
</servlet>
<servlet>
<servlet-name>SessionExample</servlet-name>
<servlet-class>SessionExample</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CompressionFilterTestServlet</servlet-name>
<url-pattern>/CompressionTest</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>HelloWorldExample</servlet-name>
<url-pattern>/servlets/servlet/HelloWorldExample</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>RequestInfoExample</servlet-name>
<url-pattern>/servlets/servlet/RequestInfoExample/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>RequestHeaderExample</servlet-name>
<url-pattern>/servlets/servlet/RequestHeaderExample</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>RequestParamExample</servlet-name>
<url-pattern>/servlets/servlet/RequestParamExample</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>CookieExample</servlet-name>
<url-pattern>/servlets/servlet/CookieExample</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>SessionExample</servlet-name>
<url-pattern>/servlets/servlet/SessionExample</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ServletToJsp</servlet-name>
<url-pattern>/servletToJsp</url-pattern>
</servlet-mapping>
<jsp-config>
<taglib>
<taglib-uri>
http://tomcat.apache.org/debug-taglib
</taglib-uri>
<taglib-location>
/WEB-INF/jsp/debug-taglib.tld
</taglib-location>
</taglib>
<taglib>
<taglib-uri>
http://tomcat.apache.org/example-taglib
</taglib-uri>
<taglib-location>
/WEB-INF/jsp/example-taglib.tld
</taglib-location>
</taglib>
<taglib>
<taglib-uri>
http://tomcat.apache.org/jsp2-example-taglib
</taglib-uri>
<taglib-location>
/WEB-INF/jsp2/jsp2-example-taglib.tld
</taglib-location>
</taglib>
<jsp-property-group>
<description>
Special property group for JSP Configuration JSP example.
</description>
<display-name>JSPConfiguration</display-name>
<url-pattern>/jsp/jsp2/misc/config.jsp</url-pattern>
<el-ignored>true</el-ignored>
<page-encoding>ISO-8859-1</page-encoding>
<scripting-invalid>true</scripting-invalid>
<include-prelude>/jsp/jsp2/misc/prelude.jspf</include-prelude>
<include-coda>/jsp/jsp2/misc/coda.jspf</include-coda>
</jsp-property-group>
</jsp-config>
<security-constraint>
<display-name>Example Security Constraint - part 1</display-name>
<web-resource-collection>
<web-resource-name>Protected Area - Allow methods</web-resource-name>
<!-- Define the context-relative URL(s) to be protected -->
<url-pattern>/jsp/security/protected/*</url-pattern>
<!-- If you list http methods, only those methods are protected so -->
<!-- the constraint below ensures all other methods are denied -->
<http-method>DELETE</http-method>
<http-method>GET</http-method>
<http-method>POST</http-method>
<http-method>PUT</http-method>
</web-resource-collection>
<auth-constraint>
<!-- Anyone with one of the listed roles may access this area -->
<role-name>tomcat</role-name>
<role-name>role1</role-name>
</auth-constraint>
</security-constraint>
<security-constraint>
<display-name>Example Security Constraint - part 2</display-name>
<web-resource-collection>
<web-resource-name>Protected Area - Deny methods</web-resource-name>
<!-- Define the context-relative URL(s) to be protected -->
<url-pattern>/jsp/security/protected/*</url-pattern>
<http-method-omission>DELETE</http-method-omission>
<http-method-omission>GET</http-method-omission>
<http-method-omission>POST</http-method-omission>
<http-method-omission>PUT</http-method-omission>
</web-resource-collection>
<!-- An empty auth constraint denies access -->
<auth-constraint />
</security-constraint>
<!-- Default login configuration uses form-based authentication -->
<login-config>
<auth-method>FORM</auth-method>
<realm-name>Example Form-Based Authentication Area</realm-name>
<form-login-config>
<form-login-page>/jsp/security/protected/login.jsp</form-login-page>
<form-error-page>/jsp/security/protected/error.jsp</form-error-page>
</form-login-config>
</login-config>
<!-- Security roles referenced by this web application -->
<security-role>
<role-name>role1</role-name>
</security-role>
<security-role>
<role-name>tomcat</role-name>
</security-role>
<!-- Environment entry examples -->
<!--env-entry>
<env-entry-description>
The maximum number of tax exemptions allowed to be set.
</env-entry-description>
<env-entry-name>maxExemptions</env-entry-name>
<env-entry-type>java.lang.Integer</env-entry-type>
<env-entry-value>15</env-entry-value>
</env-entry-->
<env-entry>
<env-entry-name>minExemptions</env-entry-name>
<env-entry-type>java.lang.Integer</env-entry-type>
<env-entry-value>1</env-entry-value>
</env-entry>
<env-entry>
<env-entry-name>foo/name1</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>value1</env-entry-value>
</env-entry>
<env-entry>
<env-entry-name>foo/bar/name2</env-entry-name>
<env-entry-type>java.lang.Boolean</env-entry-type>
<env-entry-value>true</env-entry-value>
</env-entry>
<env-entry>
<env-entry-name>name3</env-entry-name>
<env-entry-type>java.lang.Integer</env-entry-type>
<env-entry-value>1</env-entry-value>
</env-entry>
<env-entry>
<env-entry-name>foo/name4</env-entry-name>
<env-entry-type>java.lang.Integer</env-entry-type>
<env-entry-value>10</env-entry-value>
</env-entry>
<!-- Async examples -->
<servlet>
<servlet-name>async0</servlet-name>
<servlet-class>async.Async0</servlet-class>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>async0</servlet-name>
<url-pattern>/async/async0</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>async1</servlet-name>
<servlet-class>async.Async1</servlet-class>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>async1</servlet-name>
<url-pattern>/async/async1</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>async2</servlet-name>
<servlet-class>async.Async2</servlet-class>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>async2</servlet-name>
<url-pattern>/async/async2</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>async3</servlet-name>
<servlet-class>async.Async3</servlet-class>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>async3</servlet-name>
<url-pattern>/async/async3</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>stock</servlet-name>
<servlet-class>async.AsyncStockServlet</servlet-class>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>stock</servlet-name>
<url-pattern>/async/stockticker</url-pattern>
</servlet-mapping>
<!-- Non-blocking IO examples -->
<servlet>
<servlet-name>bytecounter</servlet-name>
<servlet-class>nonblocking.ByteCounter</servlet-class>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>bytecounter</servlet-name>
<url-pattern>/servlets/nonblocking/bytecounter</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>numberwriter</servlet-name>
<servlet-class>nonblocking.NumberWriter</servlet-class>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>numberwriter</servlet-name>
<url-pattern>/servlets/nonblocking/numberwriter</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.xhtml</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- Websocket examples -->
<listener>
<listener-class>websocket.drawboard.DrawboardContextListener</listener-class>
</listener>
</web-app>

View File

@@ -0,0 +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.
-->
<!DOCTYPE HTML><html lang="en"><head>
<meta charset="UTF-8">
<title>Apache Tomcat Examples</title>
</head>
<body>
<p>
<h3>Apache Tomcat Examples</H3>
<p></p>
<ul>
<li><a href="servlets">Servlets examples</a></li>
<li><a href="jsp">JSP Examples</a></li>
<li><a href="websocket/index.xhtml">WebSocket Examples</a></li>
</ul>
</body></html>

View File

@@ -0,0 +1,28 @@
<%--
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.
--%>
<%@page session="false" import="java.util.Date,java.text.SimpleDateFormat"%>
Output from async1.jsp
Type is <%=request.getDispatcherType()%>
<%
System.out.println("Inside Async 1");
if (request.isAsyncStarted()) {
request.getAsyncContext().complete();
}
Date date = new Date(System.currentTimeMillis());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
%>
Completed async request at <%=sdf.format(date)%>

View File

@@ -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.
--%>
<%@page session="false" import="java.util.Date,java.text.SimpleDateFormat"%>
Output from async3.jsp
Type is <%=request.getDispatcherType()%>
<%
Date date = new Date(System.currentTimeMillis());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
%>
Completed async 3 request at <%=sdf.format(date)%>

View File

@@ -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.
--%>
<%@page session="false"%>
<pre>
Use cases:
1. Simple dispatch
- servlet does startAsync()
- background thread calls ctx.dispatch()
<a href="<%=response.encodeURL("/examples/async/async0")%>"> Async 0 </a>
2. Simple dispatch
- servlet does startAsync()
- background thread calls dispatch(/path/to/jsp)
<a href="<%=response.encodeURL("/examples/async/async1")%>"> Async 1 </a>
3. Simple dispatch
- servlet does startAsync()
- background thread calls writes and calls complete()
<a href="<%=response.encodeURL("/examples/async/async2")%>"> Async 2 </a>
4. Simple dispatch
- servlet does a startAsync()
- servlet calls dispatch(/path/to/jsp)
- servlet calls complete()
<a href="<%=response.encodeURL("/examples/async/async3")%>"> Async 3 </a>
3. Timeout s1
- servlet does a startAsync()
- servlet does a setAsyncTimeout
- returns - waits for timeout to happen should return error page
4. Timeout s2
- servlet does a startAsync()
- servlet does a setAsyncTimeout
- servlet does a addAsyncListener
- returns - waits for timeout to happen and listener invoked
5. Dispatch to asyncSupported=false servlet
- servlet1 does a startAsync()
- servlet1 dispatches to dispatch(/servlet2)
- the container calls complete() after servlet2 is complete
- TODO
6. Chained dispatch
- servlet1 does a startAsync
- servlet1 does a dispatch to servlet2 (asyncsupported=true)
- servlet2 does a dispatch to servlet3 (asyncsupported=true)
- servlet3 does a dispatch to servlet4 (asyncsupported=false)
7. Stock ticker
<a href="<%=response.encodeURL("/examples/async/stockticker")%>"> StockTicker </a>
</pre>

View File

@@ -0,0 +1,94 @@
<%--
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.
--%>
<%@page contentType="text/html; charset=UTF-8" %>
<HTML>
<HEAD><TITLE>
Calendar: A JSP APPLICATION
</TITLE></HEAD>
<BODY BGCOLOR="white">
<%@ page language="java" import="cal.*" %>
<jsp:useBean id="table" scope="session" class="cal.TableBean" />
<%
table.processRequest(request);
if (table.getProcessError() == false) {
%>
<!-- html table goes here -->
<CENTER>
<TABLE WIDTH=60% BGCOLOR=yellow CELLPADDING=15>
<TR>
<TD ALIGN=CENTER> <A HREF=cal1.jsp?date=prev> prev </A>
<TD ALIGN=CENTER> Calendar:<%= table.getDate() %></TD>
<TD ALIGN=CENTER> <A HREF=cal1.jsp?date=next> next </A>
</TR>
</TABLE>
<!-- the main table -->
<TABLE WIDTH=60% BGCOLOR=lightblue BORDER=1 CELLPADDING=10>
<TR>
<TH> Time </TH>
<TH> Appointment </TH>
</TR>
<FORM METHOD=POST ACTION=cal1.jsp>
<%
for(int i=0; i<table.getEntries().getRows(); i++) {
cal.Entry entr = table.getEntries().getEntry(i);
%>
<TR>
<TD>
<A HREF=cal2.jsp?time=<%= entr.getHour() %>>
<%= entr.getHour() %> </A>
</TD>
<TD BGCOLOR=<%= entr.getColor() %>>
<% out.print(util.HTMLFilter.filter(entr.getDescription())); %>
</TD>
</TR>
<%
}
%>
</FORM>
</TABLE>
<BR>
<!-- footer -->
<TABLE WIDTH=60% BGCOLOR=yellow CELLPADDING=15>
<TR>
<TD ALIGN=CENTER> <% out.print(util.HTMLFilter.filter(table.getName())); %> :
<% out.print(util.HTMLFilter.filter(table.getEmail())); %> </TD>
</TR>
</TABLE>
</CENTER>
<%
} else {
%>
<font size=5>
You must enter your name and email address correctly.
</font>
<%
}
%>
</BODY>
</HTML>

View File

@@ -0,0 +1,45 @@
<%--
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.
--%>
<%@page contentType="text/html; charset=UTF-8" %>
<HTML>
<HEAD><TITLE>
Calendar: A JSP APPLICATION
</TITLE></HEAD>
<BODY BGCOLOR="white">
<jsp:useBean id="table" scope="session" class="cal.TableBean" />
<%
String time = request.getParameter ("time");
%>
<FONT SIZE=5> Please add the following event:
<BR> <h3> Date <%= table.getDate() %>
<BR> Time <%= util.HTMLFilter.filter(time) %> </h3>
</FONT>
<FORM METHOD=POST ACTION=cal1.jsp>
<BR>
<BR> <INPUT NAME="date" TYPE=HIDDEN VALUE="current">
<BR> <INPUT NAME="time" TYPE=HIDDEN VALUE="<%= util.HTMLFilter.filter(time) %>">
<BR> <h2> Description of the event <INPUT NAME="description" TYPE=TEXT SIZE=20> </h2>
<BR> <INPUT TYPE=SUBMIT VALUE="submit">
</FORM>
</BODY>
</HTML>

View File

@@ -0,0 +1,43 @@
<html>
<!--
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.
-->
<head>
<title>Untitled Document</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body bgcolor="#FFFFFF">
<p><font color="#0000FF"><a href="login.html"><img src="../images/execute.gif" align="right" border="0"></a><a href="../index.html"><img src="../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
<h2> Source Code for Calendar Example. <br>
<h3><a href="cal1.jsp.html">cal1.jsp<font color="#0000FF"></a>
</font> </h3>
<h3><a href="cal2.jsp.html">cal2.jsp<font color="#0000FF"></a>
</font> </h3>
<br>
<h2> Beans.
<h3><a href="TableBean.java.html">TableBean<font color="#0000FF"></a>
</font> </h3>
<h3><a href="Entries.java.html">Entries<font color="#0000FF"></a>
</font> </h3>
<h3><a href="Entry.java.html">Entry<font color="#0000FF"></a>
</font> </h3>
</body>
</html>

View File

@@ -0,0 +1,47 @@
<html>
<!--
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.
-->
<head>
<title> Login page for the calendar. </title>
</head>
<body bgcolor="white">
<center>
<font size=7 color="red"> Please Enter the following information: </font>
<br>
<form method=GET action=cal1.jsp>
<font size=5> Name <input type=text name="name" size=20>
</font>
<br>
<font size=5> Email <input type=text name="email" size=20>
</font>
<br>
<input type=submit name=action value="Submit">
</form>
<hr>
<font size=3 color="red"> Note: This application does not implement the complete
functionality of a typical calendar application. It demonstrates a way JSP can
be used with html tables and forms.</font>
</center>
</body>
</html>

View File

@@ -0,0 +1,56 @@
<HTML>
<!--
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.
-->
<HEAD>
<title>
checkbox.CheckTest Bean Properties
</title>
<BODY BGCOLOR="white">
<H2>
checkbox.CheckTest Bean Properties
</H2>
<HR>
<DL>
<DT>public class <B>CheckTest</B><DT>extends Object</DL>
<P>
<HR>
<P>
<TABLE BORDER="1" WIDTH="100%" CELLPADDING="3" CELLSPACING="0">
<TR BGCOLOR="#EEEEFF">
<TD COLSPAN=3><FONT SIZE="+2">
<B>Properties Summary</B></FONT></TD>
</TR>
<TR BGCOLOR="white">
<td align="right" valign="top" width="1%">
<FONT SIZE="-1">
String
</FONT></TD>
<TD><B>CheckTest:fruit</B>
<BR>
</TD>
<td width="1%">
<FONT SIZE="-1">
Multi
</FONT></TD>
</TABLE>
<HR>
</BODY>
</HTML>

Some files were not shown because too many files have changed in this diff Show More