Orion Filter Tutorial, Lesson 3, Manipulating the Response

In this part of the Orion filter tutorial, we will build a simple Filter that will be used to manipulate the response to a request.



1 Introduction

    In this part of the Orion filter tutorial, we will build a simple Filter that will be used to manipulate the response to a request.

    In order to do this we will need an HttpServletResponseWrapper, a custom ServletOutputStream and a Filter. In order to test the Filter, we will also need a JSP page to attach our filter to. We will then configure the usage of this filter, and finally test it.

    The Filter will use an HttpSerlvetResponseWrapper to wrap the Response before it is sent to the target of the request.


    Figure 1: The HttpServletResponseWrapper class.

    An HttpSerlvetResponseWrapper is an object that acts as a Wrapper or Decorator to the ServletResponse object. It is used to wrap the real Response so that we can modify it after that the target of the request has delivered its response.

    The HttpServletResponseWrapper that we are going to develop will use a custom ServletOutputStream that lets us manipulate the data after that the target of the request is done with it. Normally, this cannot be done after that the ServletOutputStream has been closed.


    Figure 2: The ServletOutputStream class.

2 Creating the FilterServletOutputStream

    Normally, the ServletOutputStream cannot be modified after a resource has committed it. We will therefore need to implement our own extension to the ServletOutputStream. Later on, you will see how we will use this FilterServletOutputStream to manipulate the response of another resource.


    Figure 3: The FilterServletOutputStream.

    We will create the FilterServletOutputStream in the following steps:



    2.1 Step 1: Create a file named 'FilterServletOutputStream.java'

      Fire up your editor and start editing a source file with the name 'FilterServletOutputStream.java'

    2.2 Step 2: Add import statements

      Start of with the package name and any imports we need, so that your source looks like the following:


      package com.acme.filter;

      import javax.servlet.ServletOutputStream;
      import java.io.DataOutputStream;
      import java.io.IOException;
      import java.io.OutputStream;
      Listing 1: The include statements.

      This will make sure that we have the neccessary classes available.

    2.3 Step 3: Add class declaration

      Continue with the following:


      public class FilterServletOutputStream extends ServletOutputStream {
      {
      private DataOutputStream stream;
      Listing 2: Extending the ServletOutputStream.

      Above we let the FilterServletOutputStream extend the normal ServletOutputStream and make sure that we keep hold of a DataOutputStream.

    2.4 Step 4: Add constructor

      Add a simple constructor to this class as described below:


      public FilterServletOutputStream(OutputStream output)
      {
      stream=new DataOutputStream(output);
      }
      Listing 3: The constructor.

      Our constructor makes sure that whenever a FilterServletOutputStream is created, we will cast the given OutputStream into a DataOutputStream.

    2.5 Step 5: Implement necessary methods

      Now lets finish this class by implementing all the various methods we need to be a valid ServletOutputStream:


      public void write(int b) throws IOException
      {
      stream.write(b);
      }

      public void write(byte[] b) throws IOException
      {
      stream.write(b);
      }

      public void write(byte[] b, int off, int len) throws IOException
      {
      stream.write(b, off, len);
      }
      }
      Listing 4: Implementing the necessary methods.

      Your FilterServletOutputStream.java should now look like this.

    2.6 Step 6: compile your FilterServletOutputStream

      Compile your FilterServletOutputStream.

      Your FilterServletOutputStream.class should be located in the 'orion/default-web-app/WEB-INF/classes/com/acme/filter/' directory when done.

3 Creating the GenericResponseWrapper

    In order to use our custom ServletOutputStream we will need to implement a class that can act as a Response object. We will send this Wrapper to the target of the request instead of the original Response object.


    Figure 4: The GenericResponseWrapper. As seen in this figure, we will implement some utility methods in the Wrapper, such as a way of retrieveing the content type and content length for the content it holds.

    We will create the GenericResponseWrapper in the following steps:



    3.1 Step 1: Create a file named 'GenericResponseWrapper.java'

      Start up your editor and start editing a source file with the name 'GenericResponseWrapper.java'

    3.2 Step 2: Add import statements

      Start of with the package name and any imports we need, so that your source looks like the following:


      package com.acme.filter;

      import javax.servlet.ServletOutputStream;
      import javax.servlet.http.HttpServletResponse;
      import javax.servlet.http.HttpServletResponseWrapper;
      import java.io.ByteArrayOutputStream;
      import java.io.PrintWriter;
      Listing 5: Import statements.

      This will make sure that we have the neccessary classes available.

    3.3 Step 3: Add class declaration

      Continue with the following:


      public class GenericResponseWrapper extends HttpServletResponseWrapper
      {
      private ByteArrayOutputStream output;
      private int contentLength;
      private String contentType;
      Listing 6: Extending the HttpServletResponseWrapper.

      Above we let the GenericResponseWrapper extend the HttpServletResponseWrapper. We also declare three instance variables; a ByteArrayOutputStream that will hold any content written by the target of the request, a int to hold the content length and a String to hold the contentType.

    3.4 Step 4: Add constructor

      Continue with adding the following constructor:


      public GenericResponseWrapper(HttpServletResponse response)
      {
      super(response);
      output = new ByteArrayOutputStream();
      }
      Listing 7: Adding a constructor.

      The constructor makes sure that the response is passed to the constructor of the HttpServletResponse. Finally, we set the output to hold a new ByteArrayOutputStream.

    3.5 Step 5: Handle binary content

      Continue with adding the following method:


      public ServletOutputStream getOutputStream()
      {
      return new FilterServletOutputStream(output);
      }
      Listing 8: Making the OutputStream available.

      The method above will be used by the target of the request when writing its binary response.

    3.6 Step 6: Retrieving the content

      Continue with adding the following method:


      public byte[] getData()
      {
      return output.toByteArray();
      }
      Listing 9: Making the content available.

      Above we add a convenient method for obtaining the data written to the ByteArrayOutputStream.

    3.7 Step 7: Handle character content

      Continue with the following method:


      public PrintWriter getWriter()
      {
      return new PrintWriter(getOutputStream(), true);
      }
      Making the PrintWriter available.

      The method above will be used by the target of the request when writing its character text response.

    3.8 Step 8: Handling content-length

      Now, anyone who told you that size does not matter is lying. If we are going to manipulate the response we need to reset its content-length.

      Continue with the following method:


      public void setContentType(String type)
      {
      this.contentType = type;
      super.setContentType(type);
      }

      public String getContentType()
      {
      return this.contentType;
      }
      Listing 10: Making the content type available.

      The methods above will be used to set and get the content length or the response. We override these method and make sure that we can obtain this value at a later time.

    3.9 Step 9: Retrieving the content-length

      Continue with the following method:


      public int getContentLength()
      {
      return contentLength;
      }

      public void setContentLength(int length)
      {
      this.contentLength=length;
      super.setContentLength(length);
      }
      LIsting 11: Making the content length available.

      The methods above can be used to set and retrieve the content length of the response.

      Your GenericResponseWrapper.java should now look like this.

      When developing your own custom Filters, you probably want to implement more functionality into your wrappers. Please feel free to use this sample as a base for such extenssions.

    3.10 Step 12: compile your Wrapper

      Compile your Wrapper.

      Your GenericResponseWrapper.class should be located in the 'orion/default-web-app/WEB-INF/classes/com/acme/filter/' directory when done.

4 Writing the Filter

    Now its time to write the Filter that will use our generic Response wrapper. This filter will just add content to the response of the target of the request before and after that the target is invoked. This Filter will extend the GenericFilter that we developed in lesson 1.

    Grab the GenericFilter from lesson 1 here.


    Figure 5: The PrePostFilter extends GenericFilter, overriding the doFilter() method.

    We will create the PrePostFilter in the following steps:



    4.1 Step 1: Create a file named 'PrePostFilter.java'

      Fire up your editor and start editing a source file with the name 'PrePostFilter.java'

    4.2 Step 2: Add import statements

      Start of with the package name and any imports we need, so that your source looks like the following:


      package com.acme.filter;

      import javax.servlet.FilterChain;
      import javax.servlet.ServletException;
      import javax.servlet.ServletRequest;
      import javax.servlet.ServletResponse;
      import javax.servlet.http.HttpServletResponse;
      import java.io.IOException;
      import java.io.OutputStream;
      Listing 12: The import statements

      This will make sure that we have the neccessary classes available.

    4.3 Step 3: Add class declaration

      Continue with the following:


      public class PrePostFilter extends GenericFilter
      {

      Listing 13: Extending the GenericFilter.

      Above we let the PrePostFilter extend the GenericFilter.

    4.4 Step 4: Override the doFilter() method

      We shall now override the doFilter() method inherited from the GenericFilter class with the following method:


      public void doFilter(final ServletRequest request, final ServletResponse response, FilterChain chain) throws IOException, ServletException
      {
      OutputStream out = response.getOutputStream();
      out.write("<HR>PRE<HR>".getBytes());
      GenericResponseWrapper wrapper = new GenericResponseWrapper((HttpServletResponse) response);
      Listing 14: Overriding the doFilter method.

      In the code above, we first print a message to our own ServletOutputStream. After that, we use our Response to create a new GenericResponseWrapper.

      Continue the method with the following lines of code:


      chain.doFilter(request, wrapper);
      out.write(wrapper.getData());
      out.write("<HR>POST<HR>".getBytes());
      out.close();
      }
      }
      Listing 15: Invoking the rest of the chain.

      In the code above, we pass on our newly created GenericResponseWrapper to the next Filter in the chain. As said before, if there are now more filters in the chain, this will invoke the initial target of the request.

      We then write the response received to our ServletOutputStream by using our wrappers getData() method, which contains the response of the Filter called previously. After that, we print out a message to our ServletOutputStream.

      Your PrePostFilter.java should now look like this.

    4.5 Step 5: compile your PrePostFilter

      Compile your PrePostFilter.

      Your PrePostFilter.class should be located in the 'orion/default-web-app/WEB-INF/classes/com/acme/filter/' directory when done.

5 Creating a JSP page to test the Filter

    Now, in order to use our filter we need to attach that filter to something. We will create a real simple JSP page and place it in the root of the default-web-app.

    We will create the JSP page in the following steps:



    5.1 step 1: Create a file named 'filter3.jsp'

      Fire up your editor so that you can create a file called 'filter3.jsp'.

    5.2 step 2: Write the source

      Add the following to your file:


      <HTML>
      <HEAD>
      <TITLE>Lesson 3</TITLE>
      </HEAD>
      <BODY>
      <P>This is a pure testpage</>
      </BODY>
      </HTML>
      Listing 16: A sample JSP page

      The JSP page above will write out a dummy message when invoked.

    5.3 step 3: Save the JSP page

      Save your file in the 'orion/default-web-app/' directory with the name 'filter3.jsp'.

      Your JSP page should now look like this.

6 Configuring your Filter

    It is now time for us to configure our Filter usage.

    To do so we will go through the following steps:



    6.1 Step 1: Configure Filter existence in web.xml

      Start up your editor and open the file 'orion/default-web-app/WEB-INF/web.xml'.

      After the configuration of the 'message' Filter, add the following rows:


      <filter>
      <filter-name>prePost</filter-name>
      <filter-class>com.acme.filter.PrePostFilter</filter-class>
      </filter>
      Listing 17: Adding the Filter.

      This will tell the container that our web-application has yet another filter that we will refer to as 'prePost', which represents the class 'com.acme.filter.PrePostFilter'.

    6.2 Step 2: Configure Filter mapping in web.xml

      After the mapping configuration of the 'message' Filter, add the following rows:


      <filter-mapping>
      <filter-name>prePost</filter-name>
      <url-pattern>/filter3.jsp</url-pattern>
      </filter-mapping>
      Listing 18: Mapping the Filter.

      This will tell our container that within this web-application, any requests for '/filter3.jsp' will first invoke the filter named 'prePost'.

    6.3 Step 3: Save changes to web.xml

      Save your 'web.xml' and make sure that it is located in the 'orion/default-web-app/WEB-INF/' directory.

      Your web.xml should now look something like this.

7 Testing your Filter

    It is now time to test our Filter.

    We will do so in the following steps:



    7.1 Step 1: Make sure Orion is running

      Make sure that your Orion Application Server is up and running.

    7.2 Step 2: Open the URL

      Open up the location 'http://localhost/filter3.jsp' in a normal browser.

      Substitute 'localhost' with the name of the server you are working with if needed.

      The result should be the content of the filter3.jsp file inside a PRE and POST printout.

    Congratulations! You have just created and successfully tested your first response-altering filter!

    Continue with Lesson 4, Manipulating a MultiMedia Response.

Copyright © 2003 IronFlare AB