Package org.archive.wayback.util.webapp

Source Code of org.archive.wayback.util.webapp.RequestFilter

/*
*  This file is part of the Wayback archival access software
*   (http://archive-access.sourceforge.net/projects/wayback/).
*
*  Licensed to the Internet Archive (IA) by one or more individual
*  contributors.
*
*  The IA licenses this file to You 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.
*/
package org.archive.wayback.util.webapp;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.LogManager;
import java.util.logging.Logger;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.archive.wayback.util.MonitoredFileSet;

/**
* Top-Level integration point between a series of RequestHandler mappings and a
* generic ServletContext. This filter is assumed to be responsible for matching
* ALL requests received by the webapp ("*") and uses a RequestMapper to
* delegate incoming HttpServletRequests to the appropriate RequestHandler, via
* the doFilter() method.
*
* @author brad
*/
public class RequestFilter implements Filter {
  private static final Logger LOGGER =
    Logger.getLogger(RequestFilter.class.getName());

  private final static String CONFIG_PATH = "config-path";
  private final static String LOGGING_CONFIG_PATH = "logging-config-path";
  private final static String MONITOR_MS_CONFIG = "monitor-ms";
  private final static String MONITOR_FILES_CONFIG = "monitor-files";

  private UpdateThread thread = null;
  private RequestMapper mapper = null;
  private ServletContext context;
  private String springConfigPath;

  public void init(FilterConfig config) throws ServletException {
    context = config.getServletContext();

    String logConfigPath = context.getInitParameter(LOGGING_CONFIG_PATH);
    if (logConfigPath != null) {
      String resolvedLogPath = context.getRealPath(logConfigPath);
      File logConfigFile = new File(resolvedLogPath);
      if (logConfigFile.exists()) {
        FileInputStream finp = null;
        try {
          finp = new FileInputStream(logConfigFile);
          LogManager.getLogManager().readConfiguration(finp);
        } catch (FileNotFoundException e) {
          e.printStackTrace();
        } catch (SecurityException e) {
          e.printStackTrace();
        } catch (IOException e) {
          e.printStackTrace();
        } finally {
          try {
            if (finp != null) {
              finp.close();
            }
          } catch (IOException e) {
            throw new ServletException(e);
          }
        }
      }
    }

    String configPath = context.getInitParameter(CONFIG_PATH);
    if (configPath == null) {
      throw new ServletException("Missing " + CONFIG_PATH + " parameter");
    }
    springConfigPath = context.getRealPath(configPath);

    String monitorFiles = context.getInitParameter(MONITOR_FILES_CONFIG);
    if(monitorFiles == null) {
      // just load once:
      mapper = loadRequestMapper();
    } else {

      // we're in fancy mode: start the background thread to watch
      // our Spring config - it will swap out our mapper when things
      // change

      String monitorMSString = context.getInitParameter(MONITOR_MS_CONFIG);
      long monitorMS = 10000;
      if(monitorMSString != null) {
        try {
          monitorMS = Long.parseLong(monitorMSString);
        } catch(NumberFormatException e) {
          throw new ServletException("Non int for " + MONITOR_MS_CONFIG);
        }
      }
      String[] monitored = monitorFiles.split(",");
 
      ArrayList<String> monitoredL = new ArrayList<String>();
      for(String monitoredPath : monitored) {
        monitoredL.add(monitoredPath);
      }
      thread = new UpdateThread(this, monitorMS, monitoredL);

      // TODO: should we force initial load of a mapper?
      //       it means incoming requests will block until we're ready..
      //       if we don't the thread will immediately being loading
      //       the Spring config, and will swap it in when it's ready
      thread.reloadMapper();
      thread.start();
    }
  }

  public void doFilter(ServletRequest request, ServletResponse response,
      FilterChain chain) throws IOException, ServletException {
    boolean handled = false;
    String origThreadName = Thread.currentThread().getName();
    try {
      if (request instanceof HttpServletRequest) {
        if (response instanceof HttpServletResponse) {
          if(mapper != null) {
            handled = mapper.handleRequest(
                (HttpServletRequest) request,
                (HttpServletResponse) response);
           
          }
        }
      }
    } finally {
      Thread.currentThread().setName(origThreadName);
    }
    if (!handled) {
      chain.doFilter(request, response);
    }
  }

  public void destroy() {
    LOGGER.info("Shutdown starting.");
    if(thread != null) {
      thread.interrupt();
    }
    if(mapper != null) {
      mapper.shutdown();
    }
    LOGGER.info("Shutdown complete.");
  }


  private RequestMapper loadRequestMapper() {
    LOGGER.info("Initializing Spring config at: " + springConfigPath);
    RequestMapper newMapper = SpringReader.readSpringConfig(springConfigPath, context);
    LOGGER.info("Initialized Spring config at: " + springConfigPath);
    return newMapper;
  }

  /**
   * @return the mapper
   */
  public RequestMapper getMapper() {
    return mapper;
  }

  /**
   * @param mapper the mapper to set
   */
  public void setMapper(RequestMapper mapper) {
    this.mapper = mapper;
  }

  /**
   * Thread that repeatedly checks a set of Spring config files. If any
   * change, then a new RequestMapper is created from them, which is then
   * swapped in on the containing RequestFilter. The old one if present is
   * shut down.
   * 
   * @author Brad Tofel
   */
  private class UpdateThread extends Thread {
    /**
     * object which merges CDX files with the BDBResourceIndex
     */
    private RequestFilter filter = null;
    private long runInterval;

    private MonitoredFileSet fileSet;
    private MonitoredFileSet.FileState activeState;

    /**
     * @param filter the RequestFilter we will update
     * @param runInterval number of MS bewtween checks
     * @param monitored List of files to check Mod Time to trigger reload
     */
    public UpdateThread(RequestFilter filter,
        long runInterval, List<String> monitored) {
     
      super("RequestFilter.UpdateThread");
      super.setDaemon(true);
      this.filter = filter;
      this.runInterval = runInterval;

      fileSet = new MonitoredFileSet(monitored);
      activeState = null;
    }
   
    public void reloadMapper() {

      MonitoredFileSet.FileState startState = fileSet.getFileState();

      RequestMapper mapper = filter.loadRequestMapper();

      if(fileSet.isChanged(startState)) {
        // erk.. files changed during the operation.. update nothing..
        LOGGER.warning("Files changed during Spring reload... discarding..");
        mapper.shutdown();

      } else {
        LOGGER.warning("Loaded RequestMapper.");
        RequestMapper oldMapper = filter.getMapper();
        filter.setMapper(mapper);
        if(oldMapper != null) {
          // shut it down (cross fingers first)
          LOGGER.warning("Shutting Down old RequestMapper.");
          oldMapper.shutdown();
        }
        activeState = startState;
      }
    }

    public void run() {
      LOGGER.info("RequestFilter.UpdateThread is alive.");
      while (true) {
        try {
          if((activeState == null) || fileSet.isChanged(activeState)) {
            reloadMapper();
          }
          sleep(runInterval);
        } catch (InterruptedException e) {
          LOGGER.info("Shutting Down.");
          return;
        }
      }
    }
  }
}
TOP

Related Classes of org.archive.wayback.util.webapp.RequestFilter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.