Request headers may be modified in a J2EE web application (webapp). By “modify” I mean to add, remove, or edit the request headers. The “hooks” are to 1) subclass javax.servlet.http.HttpServletRequestWrapper and 2) implement the javax.servlet.Filter interface. Notice that the code will be written against the standard Servlet API; this means that your code is portable (usable in other containers). Let’s step through to see how we may “modify” the request headers by adding an additional one.
First, sublcass javax.servlet.http.HttpServletRequestWrapper. The HttpServletRequestWrapper class works by using a Decorator design pattern. You will have to override two methods 1) getHeader(String name) and 2) getHeaderNames(). You will also have to provide a constructor that takes in a javax.servlet.http.HttpServletRequest object. The new class extending HttpServletRequestWrapper is called FakeHeadersRequest. This new class simply generates a request header titled “username.” The value of this request header is taken from the cookies. If a cookie with a name “username” is found, then its value is used also for the “username” request header.
/** * Copyright 2009 Jee Vang * Licensed 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.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.List; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; /** * Fake headers request object. Adds a request header * with the name "username". The value of this request header * will be taken from a cookie (also with the name, "username"). * * @author Jee Vang * */ public class FakeHeadersRequest extends HttpServletRequestWrapper { /** * Constructor. * * @param request HttpServletRequest. */ public FakeHeadersRequest(HttpServletRequest request) { super(request); } public String getHeader(String name) { //get the request object and cast it HttpServletRequest request = (HttpServletRequest)getRequest(); //if we are looking for the "username" request header if("username".equals(name)) { //loop through the cookies Cookie[] cookies = request.getCookies(); //if cookies are null, then return null if(null == cookies) { return null; } for(int i=0; i < cookies.length; i++) { //if the cookie's name is "username" if("username".equals(cookies[i].getName())) { //get its value and return it String val = cookies[i].getValue(); return val; } } } //otherwise fall through to wrapped request object return request.getHeader(name); } public Enumeration getHeaderNames() { //create an enumeration of the request headers //additionally, add the "username" request header //create a list List list = new ArrayList(); //loop over request headers from wrapped request object HttpServletRequest request = (HttpServletRequest)getRequest(); Enumeration e = request.getHeaderNames(); while(e.hasMoreElements()) { //add the names of the request headers into the list String n = (String)e.nextElement(); list.add(n); } //additionally, add the "username" to the list of request header names list.add("username"); //create an enumeration from the list and return Enumeration en = Collections.enumeration(list); return en; } } [/sourcecode] Now, we implement the javax.servlet.Filter interface in a class called SimpleFilter. SimpleFilter does nothing in the destroy() and init() methods. However, in the doFilter() method, the ServletRequest object is cast to a HttpServletRequest object, and then wrapped in the FakeHeadersRequest object. The filter chain then proceeds but with the instance of FakeHeadersRequest (instead of the instance of HttpServletRequest). [sourcecode language="java"] /** * Copyright 2009 Jee Vang * Licensed 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 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; /** * A simple filter used to create an additional header. * * @author Jee Vang * */ public class SimpleFilter implements Filter { public void destroy() { } public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { //if the ServletRequest is an instance of HttpServletRequest if(servletRequest instanceof HttpServletRequest) { //cast the object HttpServletRequest httpServletRequest = (HttpServletRequest)servletRequest; //create the FakeHeadersRequest object to wrap the HttpServletRequest FakeHeadersRequest request = new FakeHeadersRequest(httpServletRequest); //continue on in the filter chain with the FakeHeaderRequest and ServletResponse objects filterChain.doFilter(request, servletResponse); } else { //otherwise, continue on in the chain with the ServletRequest and ServletResponse objects filterChain.doFilter(servletRequest, servletResponse); } return; } public void init(FilterConfig filterConfig) throws ServletException { } } [/sourcecode] Now define the filter in your webapp's web.xml file. [sourcecode language="xml"] <!-- define the filter --> <filter> <filter-name>simpleFilter</filter-name> <filter-class>vang.jee.servlet.filter.SimpleFilter</filter-class> </filter> <!-- map the filter to a URL pattern --> <filter-mapping> <filter-name>simpleFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
Now it is time to test your filter to see if the an additional request header was added. To test the code, the following JSP is used to create a cookie named “username.” After you create the “username” cookie, simply refresh the page (press F5), and you should see all the request headers plus the “username” request headers.
<%-- Copyright 2008 Jee Vang Licensed 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 language="java" contentType="text/html; charset=utf-8"%> <%@page import="java.util.Enumeration"%> <html> <head> <script type="text/javascript"> function saveIt(name) { var x = document.forms['cookieform'].username.value; if (!x) alert('Please fill in a value in the input box.'); else { createCookie(name,x,7); } } function createCookie(name,value,days) { if (days) { var date = new Date(); date.setTime(date.getTime()+(days*24*60*60*1000)); var expires = "; expires="+date.toGMTString(); } else var expires = ""; document.cookie = name+"="+value+expires+"; path=/"; } function readIt(name) { var nameEQ = name + "="; var ca = document.cookie.split(';'); for(var i=0;i < ca.length;i++) { var c = ca[i]; while (c.charAt(0)==' ') c = c.substring(1,c.length); if (c.indexOf(nameEQ) == 0) alert(c.substring(nameEQ.length,c.length)); } } function eraseIt(name) { createCookie(name,"",-1); alert('Cookie erased'); } </script> </head> <body> <% Enumeration e = request.getHeaderNames(); while(e.hasMoreElements()) { String n = (String)e.nextElement(); String v = request.getHeader(n); out.println(n + " : " + v + "<br>\n"); } %> <form action="#" name="cookieform"> <input type="text" name="username"> <br/> <a href="javascript:saveIt('username')" class="page">Create</a><br/> <a href="javascript:readIt('username')" class="page">Read</a><br/> <a href="javascript:eraseIt('username')" class="page">Erase</a> </form> </body> </html>
Upon first loading the JSP page, your headers should look like the following. This output was produced using FireFox 3.0.6 as the browser, and Tomcat 4.1.29 as the web server.
host : localhost:8080 user-agent : Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.6) Gecko/2009011913 Firefox/3.0.6 Ubiquity/0.1.5 accept : text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 accept-language : en-us,en;q=0.5 accept-encoding : gzip,deflate accept-charset : ISO-8859-1,utf-8;q=0.7,*;q=0.7 keep-alive : 300 connection : keep-alive cache-control : max-age=0 username : null
After you enter something into the textfield and hit create, reloading the page will produce an output as follows.
host : localhost:8080 user-agent : Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.6) Gecko/2009011913 Firefox/3.0.6 Ubiquity/0.1.5 accept : text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 accept-language : en-us,en;q=0.5 accept-encoding : gzip,deflate accept-charset : ISO-8859-1,utf-8;q=0.7,*;q=0.7 keep-alive : 300 connection : keep-alive cookie : JSESSIONID=D187F63111362BEE78277F5292DEC48A; username=Jason Mraz cache-control : max-age=0 username : Jason Mraz
We are now at the end of this blog on how to modify request headers in a J2EE web application—specifically, how to add an additional header.
Pingback: Delicious Bookmarks for August 26th through August 27th « Lâmôlabs