Orion Filter Tutorial, Lesson 4, Manipulating a MultiMedia Response

In this part of the tutorial, we will build a simple Filter that manipulates a multimedia response.



1 Introduction

    In this part of the tutorial, we will build a simple Filter that manipulates a multimedia response in the form of an Image.

    The Filter will interfere with the response returned and check if it is some kind of image by checking if the content type of the Response starts with "image/" (such as in the content type "image/gif").

    If the Response is some kind of image, the Filter will try to draw the text held in the init parameter message across the image and return the manipulated image in the form of a jpeg image.

    In order to get hold of and manipulate the Response before this is sent on down the line of Filters (or to the client), The GenericResponseWrapper of lesson 3 will be used.

    The image manipulation will be carried out with the help of the AWT classes and Sun's JPEG encoder.

2 Writing the Filter

    Now its time to write the ImageFilter that will use the GenericResponseWrapper of lesson 3. The Filter will use this Wrapper to get hold of the image data and then use the neccessary AWT classes to draw a line of text across the Image. Before returning the modified Response, the Filter will use Sun's JPEG encoder to encod the Image generated into a byte array representing a JPEG image.


    Figure 1: The ImageFilter class extends the GenericFilter class.

    As this operation might change both the content type and size of the Reponse, these values has to be reset before the Response is passed on down the chain.

    We will create the ImageFilter in the following steps:



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

      Fire up your editor and start editing a source file with the name 'ImageFilter.java' in the 'filter' directory set up during lesson 1.

    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:


      import com.sun.image.codec.jpeg.JPEGImageEncoder;

      import javax.servlet.FilterChain;
      import javax.servlet.ServletException;
      import javax.servlet.ServletRequest;
      import javax.servlet.ServletResponse;
      import javax.servlet.http.HttpServletResponse;
      import java.awt.*;
      import java.awt.image.BufferedImage;
      import java.io.ByteArrayOutputStream;
      import java.io.IOException;
      import java.io.OutputStream;
      Listing 1: Package name and import 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 ImageFilter extends GenericFilter {
      Listing 2: Class declaration

      This will make sure that we extend the GenericFilter that we initially created in lesson 1.

    2.4 Step 4: Override the doFilter() method

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


      public void doFilter(final ServletRequest request, final ServletResponse response, FilterChain chain) throws IOException, ServletException
      {
      String message = getFilterConfig().getInitParameter("message");
      GenericResponseWrapper wrapper = new GenericResponseWrapper((HttpServletResponse) response);
      chain.doFilter(request, wrapper);
      OutputStream out = response.getOutputStream();
      Listing 3: Override the doFilter() method

      Above, we create a ne GenericResponseWrapper and send this on down the chain instead of the Response our Filter was originally given. This will allow us to freely setup the Response to our liking.

    2.5 Step 5: Manipulating the requested image

      Continue with the following:


      if (contentIsUncachedImage(wrapper) && message != null)
      {
      ByteArrayOutputStream imageStream = manipulateImage(message, wrapper.getData());
      response.setContentLength(imageStream.size());
      response.setContentType("image/jpeg");
      out.write(imageStream.toByteArray());
      }
      Listing 5: Override the doFilter() method (continued)

      Above, we first make sure that the Response is really an uncached image and that a message has been supplied for us to print over the image.

      If these conditions are met, we use the manipulateImage() method to get the OutputStream representing the manipulated image. With the use of this data, we set the content length of the Response.

      As you can see above, we also make sure to set the content type of the Request to "image/jpeg".

      Finally, we write the image data to the OutputStream of the Response.

    2.6 Step 6: Passing on unmodified data

      If the conditions described in step 5 above are not met, the data held by the Wrapper should be passed on without modification:


      else
      {
      out.write(wrapper.getData());
      }
      Listing 6: Override the doFilter() method (continued)

      Above, we write the unmodified data to the OuputStream of the Response.

    2.7 Step 7: Flush and close the OutputStream

      End the overriding of the doFilter() method with the following:


      out.flush();
      out.close();
      }
      Listing 7: Override the doFilter() method (continued)

      With these instructions we flush and close the OutputStream of our Response.

    2.8 Step 8: Image manipulation

      We will now rite the method that carries out the actual image manipulation. As image manipulation is outside the scope of this tutorial, there will be no code annotations.

      For those interested in image manipulation, a good exercise could be to rewrite this method into something less readable but the more effective.


      public ByteArrayOutputStream manipulateImage(String message, byte[] imageData) throws ServletException
      {
      ByteArrayOutputStream imageStream;
      Frame frame = null;
      Graphics graphics = null;
      try
      {
      frame = new Frame();
      frame.addNotify();
      MediaTracker mt = new MediaTracker(frame); // frame acts as an ImageObserver
      Image image = Toolkit.getDefaultToolkit().createImage(imageData);
      mt.addImage(image, 0);
      mt.waitForAll();
      int w = image.getWidth(frame);
      int h = image.getHeight(frame);
      BufferedImage offscreen = new BufferedImage(w, h, BufferedImage.TYPE_3BYTE_BGR);
      graphics = offscreen.getGraphics();
      graphics.drawImage(image, 0, 0, frame);
      graphics.setFont(new Font("Arial", Font.BOLD | Font.ITALIC, 30));
      graphics.setColor(Color.black);
      graphics.drawString(message, 10, 30);
      imageStream = new ByteArrayOutputStream();
      JPEGImageEncoder encoder = com.sun.image.codec.jpeg.JPEGCodec.createJPEGEncoder(imageStream);
      encoder.encode(offscreen);
      }
      catch (InterruptedException e)
      {
      throw new ServletException(e.getMessage());
      } catch (IOException e)
      {
      throw new ServletException(e.getMessage());
      }
      finally
      {
      if (graphics != null)
      {
      graphics.dispose();
      }
      if (frame != null)
      {
      frame.removeNotify();
      }
      }
      return imageStream;
      }
      Listing 8: The image manipulation (and the worst looking code sample ever seen).

    2.9 Step 9: Check for cache and image

      We will now rite the method that makes sure that the Response given to our Filter is infact an uncached image:


      public boolean contentIsUncachedImage(GenericResponseWrapper wrapper) {
      if (wrapper.getContentType().startsWith("image/")) {
      if (wrapper.getData().length > 0) {
      return true;
      }
      }
      return false;
      }
      Listing 9: cache and image control method

      Above, we first check that the content type of the Response is some kind of Image. If it is, we check that the Response holds data. If it does, our method returns true.

      Your ImageFilter should now look like this.

    2.10 Step 10: Compile the ImageFilter

      Compile the ImageFilter.

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

3 Setup an Image resource to test the Filter on

    Now, in order to test our new Filter we need an Image resource that can be requested so that we can interrupt the Response.

    Download the Image in Figure 1 above and place it in the root of the default-web-app with the name 'foobar.jpg'.

4 Configuring your Filter

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

    To do so we will go through the following steps:



    4.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 'prePost' Filter, add the following rows:


      <filter>
      <filter-name>imageManipulator</filter-name>
      <filter-class>com.acme.filter.ImageFilter</filter-class>
      <init-param>
      <param-name>message</param-name>
      <param-value>Test</param-value>
      </init-param>
      </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 'imageManipulator', which represents the class 'com.acme.filter.ImageFilter', and should have a init-param called 'message' set with the value specified.

    4.2 Step 2: Configure Filter mapping in web.xml

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


      <filter-mapping>
      <filter-name>imageManipulator</filter-name>
      <url-pattern>/*.jpg</url-pattern>
      </filter-mapping>
      Listing 18: Mapping the Filter.

      This will tell our container that within this web-application, any requests for resources with an suffix of '.jpg' will first invoke the filter named 'imageManipulator'.

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

5 Testing your Filter

    It is now time to test the Filter.

    We will do so in the following steps:



    5.1 Step 1: Make sure Orion is running

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

    5.2 Step 2: Open the URL

      Open up the location 'http://localhost/foobar.jpg' in a normal browser.

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

      The result should be the image from Figure 1 above with the text 'test' written over it.

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

    Continue with Lesson 5, Compressing the Response.

Copyright © 2003 IronFlare AB