Attachment 'TemplateNameFilter.java'

Download

   1 /*
   2  * Copyright 2003 The Apache Software Foundation.
   3  *
   4  * Licensed under the Apache License, Version 2.0 (the "License");
   5  * you may not use this file except in compliance with the License.
   6  * You may obtain a copy of the License at
   7  *
   8  *     http://www.apache.org/licenses/LICENSE-2.0
   9  *
  10  * Unless required by applicable law or agreed to in writing, software
  11  * distributed under the License is distributed on an "AS IS" BASIS,
  12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13  * See the License for the specific language governing permissions and
  14  * limitations under the License.
  15  */
  16 
  17 package org.apache.velocity.tools.view.servlet;
  18 
  19 import javax.servlet.Filter;
  20 import javax.servlet.FilterConfig;
  21 import javax.servlet.ServletException;
  22 import javax.servlet.ServletRequest;
  23 import javax.servlet.ServletResponse;
  24 import javax.servlet.FilterChain;
  25 import javax.servlet.ServletContext;
  26 import javax.servlet.RequestDispatcher;
  27 import javax.servlet.http.HttpServletRequest;
  28 import java.io.IOException;
  29 import java.util.Enumeration;
  30 import java.util.LinkedList;
  31 import java.util.Set;
  32 import java.util.Iterator;
  33 import java.util.HashSet;
  34 
  35 /**
  36  * <p>This class is a forwarding filter which allows to hide ".vtl" from resource URLs.
  37  * It works by building a cache of all template names in the webapp, and adding </p>
  38  *
  39  * <p>The purpose of this feature is to allow URLs to be independant of the status of the resource:
  40  * regular file or template file, allowing this status to transparently change over time.
  41  *  You can store all resources in the same directory tree, templates having
  42  * an additional ".vtl" like in "foo.html.vtl" or "bar.js.vtl".</p>
  43  *
  44  * <p>In development mode, you can choose either to reset the cache periodically,
  45  * or manually with the "reset-cache" URI, or both.</p>
  46  *
  47  * <p>Initialization parameters:
  48  * <ul>
  49  * <li>template-extension: the targeted template extension (default: ".vtl").</li>
  50  * <li>reset-method: "periodic" or "manual" or "both" or "none" (default: "none").<li>
  51  * <li>reset-uri: the rest uri, for manual resets (default: "/reset-cache").
  52  * <li>reset-period: the period, in seconds, between two resets, for periodic resets (default: 120s).</li>
  53  * </ul>
  54  * </p>
  55  *
  56  * @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
  57  *
  58  */
  59 
  60 public class TemplateNameFilter implements Filter {
  61 
  62     private ServletContext servletContext;
  63 
  64     /* targeted template extension */
  65     private String templateExtension = ".vtl";
  66 
  67     /* reset method */
  68     private static final int RESET_NONE = 0;
  69     private static final int RESET_MANUAL = 1;
  70     private static final int RESET_PERIODIC = 2;
  71     private int resetMethod = RESET_NONE; /* bit-masked */
  72 
  73     /* reset uri */
  74     private String resetUri = "/reset-cache";
  75 
  76     /* reset period */
  77     private long resetPeriod = 120000; /* millisec */
  78 
  79     /* the set of template names */
  80     private Set templates = null;
  81 
  82     /* the time of the last reset */
  83     private long lastReset;
  84 
  85     /**
  86      * init the filter
  87      * @param filterConfig filter configuration
  88      * @throws ServletException
  89      */
  90     public void init(FilterConfig filterConfig) throws ServletException {
  91         servletContext = filterConfig.getServletContext();
  92 
  93         /* init parameters */
  94         String param,value;
  95         Enumeration params = filterConfig.getInitParameterNames();
  96         while(params.hasMoreElements()) {
  97             param = (String)params.nextElement();
  98             value = filterConfig.getInitParameter(param);
  99             if ("template-extension".equals(param)) {
 100                 if (!value.startsWith(".")) {
 101                     value = "." + value;
 102                 }
 103                 templateExtension = value;
 104             } else if ("reset-method".equals(param)) {
 105                 if ("manual".equals(value)) {
 106                   resetMethod = RESET_MANUAL;
 107                 } else if ("periodic".equals(value)) {
 108                     resetMethod = RESET_PERIODIC;
 109                 } else if ("both".equals(value)) {
 110                     resetMethod = RESET_MANUAL | RESET_PERIODIC;
 111                 } else if (!"none".equals(value)) {
 112                     servletContext.log("[warn] TemplateNameFilter: reset-method should be one of 'none', 'manual', 'pediodic' or 'both'.");
 113                 }
 114             } else if ("request-uri".equals(param)) {
 115                 resetUri = value;
 116             } else if ("reset-period".equals(param)) {
 117                 try {
 118                     resetPeriod = Integer.parseInt(value) * 1000;
 119                 } catch (NumberFormatException nfe) {
 120                     servletContext.log("[warn] TemplateNameFilter: reset-period should be a number!");
 121                 }
 122             } else {
 123                 servletContext.log("[warn] TemplateNameFilter: unknown parameter '"+param+"' ignored.");
 124             }
 125         }
 126 
 127         /* builds the cache */
 128         buildsTemplateNamesList();
 129     }
 130 
 131     /**
 132      * Builds the cache, which consists of a hash set containing all template names.
 133      *
 134      */
 135     private synchronized void buildsTemplateNamesList() {
 136         /* check again if the reset is necessary, the current thread may have been
 137         waiting to enter this method during the last reset */
 138         if ((resetMethod & RESET_PERIODIC) != 0 && System.currentTimeMillis() - lastReset < resetPeriod && templates != null) {
 139             return;
 140         }
 141 
 142         Set result = new HashSet();
 143 
 144         String path,entry;
 145         Set entries;
 146         LinkedList paths = new LinkedList();
 147         paths.add("/");
 148         while(paths.size() > 0) {
 149             path = (String)paths.removeFirst();
 150             entries = servletContext.getResourcePaths(path);
 151             for (Iterator i = entries.iterator();i.hasNext();) {
 152                 entry = (String)i.next();
 153                 /* ignore some entries... */
 154                 if (entry.endsWith("/WEB-INF/") || entry.endsWith("/.svn/")) {
 155                     continue;
 156                 }
 157                 if (entry.endsWith("/")) {
 158                     paths.add(entry);
 159                 }
 160                 else if (entry.endsWith(templateExtension)) {
 161                     result.add(entry.substring(0,entry.length()-templateExtension.length()));
 162                 }
 163             }
 164         }
 165         templates = result;
 166         lastReset = System.currentTimeMillis();
 167     }
 168 
 169     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException {
 170 
 171         HttpServletRequest request = (HttpServletRequest)servletRequest;
 172 
 173         String path = request.getRequestURI();
 174 
 175         /* I've been said some buggy containers where leaving the query string in the uri */
 176         int i;
 177         if ((i = path.indexOf("?")) != -1) {
 178             path = path.substring(0,i);
 179         }
 180 
 181         /* is it time for a reset ? */
 182         long now = System.currentTimeMillis();
 183         if ((resetMethod & RESET_MANUAL) != 0 && path.equals(resetUri)) {
 184             lastReset = now - 2*resetPeriod;
 185             buildsTemplateNamesList();
 186         } else if ((resetMethod & RESET_PERIODIC) != 0 && now - lastReset > resetPeriod) {
 187             buildsTemplateNamesList();
 188         }
 189 
 190         if(templates.contains(path)) {
 191             /* forward the request with extension added */
 192             RequestDispatcher dispatcher = servletContext.getRequestDispatcher(path+templateExtension);
 193             dispatcher.forward(request,servletResponse);
 194         } else {
 195             /* normal processing, but issue a warning if we see the extension we're supposed to hide */
 196             if(path.endsWith(templateExtension)) {
 197                 servletContext.log("[warn] TemplateNameFilter: uri '"+path+"' contains the extension I'm supposed to hide. Is it normal?");
 198             }
 199             filterChain.doFilter(servletRequest,servletResponse);
 200         }
 201     }
 202 
 203     public void destroy() {
 204     }
 205 }

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.

You are not allowed to attach a file to this page.