260 likes | 517 Views
Servlet Filters. L. Grewe. Filters. New with Servlet Specification 2.3 Lightweight framework for filtering dynamic or static content A filter is a reusable piece of code that can transform the content of HTTP requests, responses, and header information. Example uses: Authentication filters
E N D
Servlet Filters L. Grewe
Filters • New with Servlet Specification 2.3 • Lightweight framework for filtering dynamic or static content • A filter is a reusable piece of code that can transform the content of HTTP requests, responses, and header information. • Example uses: • Authentication filters • Logging and auditing filters • Image conversion filters • Data compression filters • Encryption filters • XSL/T filters that transform XML content • Caching filters
servlet filter request request response response Servlets: Filters Transform HTTP requests and responses
Example 1: Facebook Authenticaton • You have a facebook application built with various jsps and/or servlets. • Before someone can use your facebook application they must be logged into facebook as an authenticated user before your application can get any user data from facebook about them. • SOLUTION 1: • Create a servlet that serves as the “interface servlet” (callback url) between facebook and your application. This servlet must make sure that the user is logged into facebook/autheticated and if not forwards them to a URL for facebook log- in. • PROBLEM with SOLUTION: • Now need separate Servlet layer for each time user makes request of your app. • BETTER SOLUTION:……..FILTERS……next….
Facebook Authenticaton w/ Filters • Instead of a separate servlet, create a filter used before each of your apps jsps/servlets. • This filter, FaceBookAuthFilter, makes sure the user is authentic and • if so passes on this information to your main apps jsps/servlets as parameters in request. • If NOT then forwards request on to facebook log-in page.
request request response FaceBookAuthFilter Setup Facebook App JSPs/servlets FaceBookAuthFilter OR response request Facebook login
FaceBookAuthFilter Code (see website for complete code) /** * The servlet filter that makes sure that the user is logged in before * letting the requests reach the application code. * @author theliveweb.net * */ public class FaceBookAuthFilter implements Filter { private String _apiKey; private String _secretKey; //read in some parameters passed via web.xml file //these parameters are unique to each facebook application and // are required when you make facebook api calls in your webapp public void init(final FilterConfig filterConfig){ _apiKey = filterConfig.getInitParameter("api_key"); _secretKey = filterConfig.getInitParameter("secret_key"); }
/** * Verifies whether user is logged in. If not, sends user to the login page. */ public void doFilter(final ServletRequest request, final ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpReq = (HttpServletRequest) request; HttpServletResponse httpRes = (HttpServletResponse) response; try { //determine if user authenticated and if not throw an exception httpReq.getParameter(FacebookParam.SESSION_KEY.toString())+"<br><br>"); FacebookXmlRestClient authClient = FaceBookAuthHandler.getAuthenticatedClient(httpReq, _apiKey, _secretKey); //if user authenticated set as request param and call next filter or the Servlet request.setAttribute("facebook.client", authClient); chain.doFilter(request, response); } catch (FailedLoginException fle) { //user not logged in, this will forward request to facebook login page forceLogin(httpRes); } catch (Exception e) { //handle other exceptions } }
Facebook example…partial web.xml file Declare our web-app servlets/ jsps use our FaceBookAuthFilter through the web.xml file <display-name>Facebook Suchana</display-name> <filter> <filter-name> FaceBookAuthFilter </filter-name> <filter-class> net.theliveweb.facebook.FaceBookAuthFilter </filter-class> <init-param> <param-name>api_key</param-name> <param-value>b70966a3bbf411cd67e12b052f159e9a</param-value> </init-param> <init-param> <param-name>secret_key</param-name> <param-value>3eac91f71a179f810d4e2495cc3bace1</param-value> </init-param> </filter> <filter-mapping> <filter-name>FaceBookAuthFilter</filter-name> <url-pattern>/T.jsp</url-pattern> </filter-mapping> <filter-mapping> <filter-name>FaceBookAuthFilter</filter-name> <url-pattern>/index.jsp</url-pattern> </filter-mapping> <filter-mapping> <filter-name>FaceBookAuthFilter</filter-name> <url-pattern>/avatar</url-pattern> </filter-mapping> <servlet> <servlet-name>avatar</servlet-name> <jsp-file>/avatar.jsp</jsp-file> </servlet> <servlet> <servlet-name>T</servlet-name> <jsp-file>/T.jsp</jsp-file> </servlet> Here we are declaring the filter, you can have more than one if you want. Here we are saying whatfilters are applied to whichwebapp url patterns Here we have areservlet declarations…only partial info here
Filter class methods: • init • called before the servlet engine begins using the filter. • destroy • called before the engine removes a filter from service. If you need to clean up filter-specific resources, you can do that with the destroy method. • doFilter • meat of the filter, call each time user invokes filtered servlets/jsps • where you have access to the request and response objects, just as you would in a normal servlet's doGet or doPost method. You can query or modify these objects as needed. • Then, you can forward the request to the next filter in the chain (or to the servlet if this is deployed as the last filter) by calling filterChain.doFilter.
Generic Filter deployment • Deploy this class with webapp that use this filter. • Modify the webapp’s web.xml • Add filter definition to bottom of list of filters (if any): <filter><filter-name>Request Blocker</filter-name><filter-class>com.develop.filters.RequestBlocker</filter-class></filter> • Apply filter to any servlets/jsps by indicating the url-pattern: <filter-mapping><filter-name>Request Blocker</filter-name><url-pattern>/*</url-pattern></filter-mapping>
Filters can Modify BOTH the request and response objects! • Our facebook example modified the request parameter…..lets look at an example that modifies the response parameter……
Example 2: XSLTFilter • Automatically performs a transform on an XML document returned the servlet it calls (chains to), rendering the document into HTML before returning it to the caller.
How XSLTFilter Works • STEPS of doFilter() • Setup A SPECIAL response object we can use easily for translation: • Instead of passing the response object to the next filter in the chain, XSLTFilter's call to doFilter specifies a customized response object named wrappedResp • To make it easy to replace the response object, the filter architecture provides a helper class, named HttpServletResponseWrapper, that wraps the original response object, and simply passes through every method call. XSLTFilter creates an anonymous subclass of HttpServletResponseWrapper, overriding three methods: getOutputStream, getWriter, and setContentType. • The getOutputStream and getWriter methods use an instance of the nested helper class ByteArrayPrintWriter, named pw. • Call downstream filter or Servlet • When a downstream filter or servlet writes into the "response," it actually writes into the instance of pw (which is the writer for the wrappedResp object)AND this pw object is stored in the Filter class. • Change response type to “text/html” • The setContentType method checks to see if the content being returned is "text/xml". If a downstream servlet or filter tries to set the content type to "text/xml", the overridden setContentType changes it to "text/html" instead, and sets a flag, xformNeeded[0], indicating that the transform needs to run. • Translate current response from xml to html • After calling filerChain.doFilter, XSLTFilter checks to see if it needs to transform the response. If it does, it takes the downstream response from pw, and transforms it into the "real" response, resp. Of course, resp might not be the "real" response either, because XSLTFilter might be downstream from yet another filter. • Transformation done w/ XSLT, which is loaded using the Java API for XML Parsing (JAXP) TransformerFactory class. Setting up the transform illustrates another filter feature: initialization parameters. The XSLTFilter expects to be configured with an initialization parameter named "xslt" that specifies which transform to run.
XSLTFilter code //class com.develop.filters.XSLTFilter package com.develop.filters; import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; import javax.xml.transform.*; import javax.xml.transform.stream.*; public class XSLTFilter implements Filter { private ServletContext ctx; private String xslt; private TransformerFactory tf = TransformerFactory.newInstance(); private Transformer xform; private static class ByteArrayServletStream extends ServletOutputStream { ByteArrayOutputStream baos; ByteArrayServletStream(ByteArrayOutputStream baos) { this.baos = baos; } public void write(int param) throws java.io.IOException { baos.write(param); } }
private static class ByteArrayPrintWriter { private ByteArrayOutputStream baos = new ByteArrayOutputStream(); private PrintWriter pw = new PrintWriter(baos); private ServletOutputStream sos = new ByteArrayServletStream(baos); public PrintWriter getWriter() { return pw; } public ServletOutputStream getStream() { return sos; } byte[] toByteArray() { return baos.toByteArray(); } } public void init(FilterConfig filterConfig) throws ServletException { ctx = filterConfig.getServletContext(); xslt = filterConfig.getInitParameter("xslt"); ctx.log("Filter " + filterConfig.getFilterName() + " using xslt " + xslt); try { xform = tf.newTransformer(new StreamSource( ctx.getResourceAsStream(xslt))); } catch (Exception e) { ctx.log("Could not intialize transform", e); throw new ServletException( "Could not initialize transform", e); } }
public void doFilter(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse, javax.servlet.FilterChain filterChain) throws java.io.IOException, javax.servlet.ServletException { HttpServletRequest hsr = (HttpServletRequest)servletRequest; final HttpServletResponse resp = (HttpServletResponse)servletResponse; ctx.log("Accessing filter for " + httpReqLine(hsr) + " " + hsr.getMethod()); final ByteArrayPrintWriter pw = new ByteArrayPrintWriter(); final boolean[] xformNeeded = new boolean[1]; //STEP 1 – SETUP SPECIAL RESPONSE OBJECT HttpServletResponse wrappedResp = new HttpServletResponseWrapper(resp) { public PrintWriter getWriter() { return pw.getWriter(); } public ServletOutputStream getOutputStream() { return pw.getStream(); } public void setContentType(String type) { if (type.equals("text/xml")) { ctx.log("Converting xml to html"); resp.setContentType("text/html"); xformNeeded[0] = true; } else { resp.setContentType(type); } } }; //STEP 2 – call downstream filter or Servlet filterChain.doFilter(servletRequest, wrappedResp); //STEP 3: set content type of response Call chain to get xml data
byte[] bytes = pw.toByteArray(); if (bytes == null || (bytes.length == 0)) { ctx.log("No content!"); } if (xformNeeded[0] == true) { try { //Note: This can be _very_ inefficient for large //transforms such transforms should be pre- //calculated. ByteArrayOutputStream baos = new ByteArrayOutputStream(); xform.transform(new StreamSource(new ByteArrayInputStream(bytes)), new StreamResult(baos)); byte[] xformBytes = baos.toByteArray(); /*This fixes a bug in the original published tip, which did not set the content length to the _new_ length implied by the xform. */ resp.setContentLength(xformBytes.length); resp.getOutputStream().write(xformBytes); ctx.log("XML -> HTML conversion completed"); } catch (Exception e) { throw new ServletException("Unable to transform document", e); } } else { resp.getOutputStream().write(bytes); } } public void destroy() { ctx.log("Destroying filter..."); } } STEP 4: Translate xml to HTML Writing out the newly Transformed xml to html Data to response
public String httpReqLine(HttpServletRequest req) { StringBuffer ret = req.getRequestURL(); String query = req.getQueryString(); if (query != null) { ret.append("?").append(query); } return ret.toString(); } //get header info public String getHeaders(HttpServletRequest req) throws IOException { Enumeration en = req.getHeaderNames(); StringBuffer sb = new StringBuffer(); while (en.hasMoreElements()) { String name = (String) en.nextElement(); sb.append(name).append(": ").append( req.getHeader(name)).append("\n"); } return sb.toString(); }
XSLTransform Filter web.xml partial file <filter> <filter-name>XSLT Filter</filter-name> <filter-class>com.develop.filters.XSLTFilter</filter-class> <init-param> <param-name>xslt</param-name> <!-- Change the param-value to the XSLT you want to use --> <param-value>/xform2.xsl</param-value> </init-param> </filter> <filter-mapping> <filter-name>XSLT Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> NOTE: The transform is performed using XSLT, which is loaded using the Java API for XML Parsing (JAXP) TransformerFactory class. Setting up the transform illustrates another filter feature: initialization parameters. The XSLTFilter expects to be configured with an initialization parameter named "xslt" that specifies which transform to run.
XSLTransform example….the rest • Make sure that your servlet engine is configured to set the content type for XML files. In Tomcat you will edit the {yourTomcat}/conf/web.xml <!-- add to the list of mime-mappings already present --> <mime-mapping> <extension>xml</extension> <mime-type>text/xml</mime-type> </mime-mapping> • Install some XML and XSL files. The web.xml before assumes that you are using xform2.xsl and next slide is an Index.xml to do the filter on. Copy the files to {YourTomcat}/webapps/examples
Index.xml to do the translation on This is the XML: document root tag is tips, which has2 tips in it and 2 authors <?xml version="1.0" encoding="iso-8859-1"?> <!-- File Index.xml --> <tips> <author id="stu" fullName="Stuart Halloway"/> <author id="glen" fullName="Glen McCluskey"/> <tip title="Using the SAX API" author="stu" htmlURL="http://java.sun.com/jdc/TechTips/2000/tt0627.html#tip2" textURL="http://java.sun.com/jdc/TechTips/txtarchive/June00_Stu.txt"> </tip> <tip title="Random Access for Files" author="glen" htmlURL="http://java.sun.com/jdc/TechTips/2000/tt0509.html#tip1" textURL="http://java.sun.com/jdc/TechTips/txtarchive/May00_GlenM.txt"> </tip> </tips> <!-- File Xform2.xsl --> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/"> <HTML><BODY><H1>JDC Tech Tips Archive</H1> <xsl:apply-templates/> </BODY></HTML> </xsl:template> <!-- list the title of a tip --> < xsl:template match="tip"> <br><xsl:apply-templates select="@*"/><xsl:value-of select="@title"/></br> </xsl:template> <!-- create a link to any htmlURL --> <xsl:template match="@htmlURL"> <A HREF="/developer/JDCTechTips/2001/{.}"> HTML </A> | </xsl:template> <!-- create a link to any textURL --> <xsl:template match="@textURL"> <A HREF="/developer/JDCTechTips/2001/{.}"> TEXT </A> | </xsl:template> <!-- ignore other attributes --> <xsl:template match="@*"/> </xsl:stylesheet> This is the XSL for use in translating the xml to html