FilterHandler.java :  » Web-Server » Brazil » sunlabs » brazil » filter » Java Open Source

Java Open Source » Web Server » Brazil 
Brazil » sunlabs » brazil » filter » FilterHandler.java
/*
 * FilterHandler.java
 *
 * Brazil project web application Framework,
 * export version: 1.1 
 * Copyright (c) 1999-2001 Sun Microsystems, Inc.
 *
 * Sun Public License Notice
 *
 * The contents of this file are subject to the Sun Public License Version 
 * 1.0 (the "License"). You may not use this file except in compliance with 
 * the License. A copy of the License is included as the file "license.terms",
 * and also available at http://www.sun.com/
 * 
 * The Original Code is from:
 *    Brazil project web application Framework release 1.1.
 * The Initial Developer of the Original Code is: suhler.
 * Portions created by suhler are Copyright (C) Sun Microsystems, Inc.
 * All Rights Reserved.
 * 
 * Contributor(s): cstevens, suhler.
 *
 * Version:  1.17
 * Created by suhler on 99/07/29
 * Last modified by suhler on 01/01/14 14:52:11
 */

package sunlabs.brazil.filter;

import sunlabs.brazil.server.Handler;
import sunlabs.brazil.server.Request;
import sunlabs.brazil.server.Server;
import sunlabs.brazil.server.ChainHandler;

import sunlabs.brazil.util.http.MimeHeaders;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Vector;
import java.util.StringTokenizer;
import java.util.Properties;


/**
 * The <code>FilterHandler</code> captures the output of some 
 * <code>Handler</code> and then applies an number of
 * {@link sunlabs.brazil.filter.Filter Filters} to change that output
 * before it is returned to the client.
 * <p>
 * This handler provides one of the core services now associated with
 * the Brazil Server: the ability to dynamically rewrite web content
 * obtained from an arbitrary source.
 * <p>
 * For instance, the <code>FilterHandler</code> can be used as a proxy for
 * a PDA.  The wrapped <code>Handler</code> would go to the web to
 * obtain the requested pages on behalf of the PDA.  Then, a 
 * <code>Filter</code> would examine all "text/html" pages and rewrite the
 * pages so they fit into the PDA's 200 pixel wide screen.  Another
 * <code>Filter</code> would examine all requested images and dynamically
 * dither them to reduce the wireless bandwidth consumed by the PDA.
 * <p>
 * The following configuration parameters are used to initialize this
 * <code>Handler</code>: <dl class=props>
 *
 * <dt> <code>prefix</code>
 * <dd> Only URLs beginning with this string will be candidates for
 *  filtering.  The default is "/".
 *
 * <dt> <code>handler</code>
 * <dd> The name of the <code>Handler</code> whose output will be captured
 *  and then filtered.  This is called the "wrapped handler".  
 *
 * <dt> <code>filters</code>
 * <dd> A list of <code>Filter</code> names.   The filters are applied in
 *  the specified order to the output of the wrapped handler.
 * <dt> <code>exitOnError</code>
 * <dd> If set, the server's <code>initFailure</code> will set
 *  any of the filters fail to 
 *  initialize.  No handler prefix is required.
 * </dl>
 *
 * A sample set of configuration parameters illustrating how to use this
 * handler follows:
 * <pre>
 * handler=filter
 * port=8081
 *
 * filter.class=sunlabs.brazil.filter.FilterHandler
 * filter.handler=proxy
 * filter.filters=noimg
 *
 * proxy.class=sunlabs.brazil.proxy.ProxyHandler
 *
 * noimg.class=sunlabs.brazil.filter.TemplateFilter
 * noimg.template=sunlabs.brazil.template.NoImageTemplate
 * </pre>
 * These parameters set up a proxy server running on port 8081.  As with a
 * normal proxy, this proxy server forwards all HTTP requests to the target
 * machine, but it then examines all HTML pages before they are returned to
 * the client and strips out all <code>&lt;img&gt;</code> tags.  By applying
 * different filters, the developer could instead build a server <ul>
 * <li> to automatically dither embedded images down to grayscale (instead
 *  of simply stripping them all out)
 * <li> to apply pattern recognition techniques to strip out only the
 *  advertisements
 * <li> to examine and change arbitrary URLs on the page
 * <li> to extract the content from an HTML page and dynamically combine it
 *  with another file to produce a different look-and-feel.
 * </ul>
 * 
 * @author  Stephen Uhler (stephen.uhler@sun.com)
 * @author  Colin Stevens (colin.stevens@sun.com)
 * @version  1.17 01/01/14
 */

public class FilterHandler
    implements Handler
{
    private static final String URL_PREFIX = "prefix";
    private static final String HANDLER = "handler";
    private static final String FILTERS = "filters";

    String prefix;
    
    String urlPrefix = "/";
    public Handler handler;
    public Filter[] filters;

    /**
     * Start the handler and filter classes.
     */

    public boolean
    init(Server server, String prefix)
    {
  this.prefix = prefix;
  
  String str;

  Properties props = server.props;
  
  urlPrefix = props.getProperty(prefix + URL_PREFIX, urlPrefix);
  boolean exitOnError= (props.getProperty("exitOnError") != null);

  /*
   * Start the handler to fetch the content.
   */

  str = props.getProperty(prefix + HANDLER, "");
  handler = ChainHandler.initHandler(server, prefix + HANDLER + ".",
    str);
  if (handler == null) {
      return false;
  }
  server.log(Server.LOG_DIAGNOSTIC, prefix, "using handler: " + str);

  /*
   * Gather the filters.
   */

  str = props.getProperty(prefix + FILTERS, "");
  StringTokenizer names = new StringTokenizer(str);
  Vector v = new Vector();
  while (names.hasMoreTokens()) {
      String name = names.nextToken();
      server.log(Server.LOG_DIAGNOSTIC, prefix, "using filter: " + name);

      Filter f = initFilter(server, prefix, name);
      if (f != null) {
    v.addElement(f);
      } else if (exitOnError) {
    server.log(Server.LOG_ERROR, prefix,
      "filter: " + name + " didn't start");
    server.initFailure=true;
      }
  }
  if (v.size() == 0) {
      server.log(Server.LOG_DIAGNOSTIC, prefix, "no filters");
      return false;
  }
  filters = new Filter[v.size()];
  v.copyInto(filters);
  return true;
    }

    private static Filter
    initFilter(Server server, String prefix, String name)
    {
  String className = server.props.getProperty(name + ".class");
  if (className == null) {
      className = name;
  } else {
      prefix = null;
  }
  if (prefix == null) {
      prefix = name + ".";
  }

  try {
      Filter f = (Filter) Class.forName(className).newInstance();
      server.log(Server.LOG_DIAGNOSTIC, prefix, "Creating: " + className);
      if (f.init(server, prefix)) {
    return f;
      }
      server.log(Server.LOG_WARNING, name, "did not initialize");
  } catch (ClassNotFoundException e) {
      server.log(Server.LOG_WARNING, prefix, "no such class:" + e);
  } catch (IllegalArgumentException e) {
      server.log(Server.LOG_WARNING, prefix, "Invalid argument" + e);
  } catch (ClassCastException e) {
      server.log(Server.LOG_WARNING, prefix, "is not a Filter");
  } catch (Exception e) {
      server.log(Server.LOG_WARNING, prefix, "error initializing");
      e.printStackTrace();
  }
  return null;
    }

    

    /**
     * Responds to an HTTP request by the forwarding the request to the
     * wrapped <code>Handler</code> and filtering the output of that
     * <code>Handler</code> before sending the output to the client.
     * <p>
     * At several stages, the <code>Filters</code> are given a chance to
     * short-circuit this process: <ul>
     *
     * <li> Each <code>Filter</code> is given a chance to examine the
     * request before it is sent to the <code>Handler</code>.  The
     * <code>Filter</code> may decide to change the request's properties.  
     * A <code>Filter</code> may even return some content to the client now,
     * in which case, neither the <code>Handler</code> nor any further
     * <code>Filter</code>s are invoked at all.
     *
     * <li> After the <code>Handler</code> has generated the response headers,
     * but before it has generated any content, each <code>Filter</code> is
     * asked if it would be interested in filtering the content.  If no
     * <code>Filter</code> is, then the subsequent content from the
     * <code>Handler</code> will be sent directly to the client.
     *
     * <li> On the other hand, if any <code>Filter</code> <b>is</b> interested
     * in filtering the content, then the output of the <code>Handler</code>
     * will be sent to each of the interested <code>Filter</code>s in order.
     * The output of each interested <code>Filter</code> is sent to the
     * next one; the output of the final <code>Filter</code> is sent to
     * the client.  At this point, any one of the invoked <code>Filter</code>s
     * can decide to reject the content completely, instead of rewriting it.
     * </ul>
     *
     * @param  request
     *    The HTTP request to be forwarded to one of the sub-servers.
     *
     * @return  <code>true</code> if the request was handled and content
     *    was generated, <code>false</code> otherwise.  
     *
     * @throws  IOException
     *    if there was an I/O error while sending the response to
     *    the client. 
     */

    public boolean
    respond(Request request)
  throws IOException
    {
      if (request.url.startsWith(urlPrefix) == false) {
      return false;
      }  

  /*
   * Let each filter get a crack at the request as a handler.
   */

  for (int i = 0; i< filters.length; i++) {
      if (filters[i].respond(request)) {
    return true;
      }
  }

  /*
   * Capture output from handler.  When the handler is done, run the
   * filters.
   */

  FilterStream out = new FilterStream(request.out);
  request.out = out;

  try {
      if (handler.respond(request) == false) {
    request.log(Server.LOG_DIAGNOSTIC, prefix,
      "No output from handler - skipping filters");
    return false;
      }
      if (out.shouldFilter) {
    return out.applyFilters(request);
      } else {
    /*
     * handler.respond() has already sent the response.
     */
    return true;
      }
  } finally {
      out.restore(request);
  }
    }

    private class FilterStream
  extends Request.HttpOutputStream
    {
  boolean shouldFilter;
  Request.HttpOutputStream old;

  int count;
  Filter[] postFilters;

  public
  FilterStream(Request.HttpOutputStream old)
  {
      super(new ByteArrayOutputStream());

      this.old = old;
  }

  /**
   * Check if any of the filters want to filter the data, based on
   * what's in the HTTP headers.  If none of them do, then we don't
   * have to filter the data at all, so restore the request's original
   * output stream.
   */

  public void
  sendHeaders(Request request)
      throws IOException
  {
      postFilters = new Filter[filters.length];
      
      for (int i = 0; i < filters.length; i++) {
    Filter f = filters[i];
    if (f.shouldFilter(request, request.responseHeaders)) {
        postFilters[count++] = f;
    }
      }

      if (count == 0) {
    /*
     * Based on the HTTP response headers, no filters want to
     * process the content, so restore orginal output stream.
     */

    request.log(Server.LOG_DIAGNOSTIC, prefix, 
      "no filters activated");
    restore(request);
    old.sendHeaders(request);
      } else {
    /*
     * Disable chunked encoding, so we get the content as bytes
     * not as chunks.  We want only bytes so we can later apply
     * the filters.
     */

    request.version = 10;
    shouldFilter = true;
      }
  }

  public boolean
  applyFilters(Request request)
      throws IOException
  {
      request.out.flush();
      restore(request);

      byte[] content = ((ByteArrayOutputStream) out).toByteArray();
      for (int i = 0; i < count; i++) {
    content = postFilters[i].filter(request,
      request.responseHeaders, content);
    if (content == null) {
        return false;
    }
      }

      request.sendResponse(content, null);
      return true;
  }

  public void
  restore(Request request)
  {
      request.out = old;
  }
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.