/* * Copyright (C) 2000 - 2011 TagServlet Ltd * * This file is part of Open BlueDragon (OpenBD) CFML Server Engine. * * OpenBD is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * Free Software Foundation,version 3. * * OpenBD is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenBD. If not, see http://www.gnu.org/licenses/ * * Additional permission under GNU GPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or combining * it with any of the JARS listed in the README.txt (or a modified version of * (that library), containing parts covered by the terms of that JAR, the * licensors of this Program grant you additional permission to convey the * resulting work. * README.txt @ http://www.openbluedragon.org/license/README.txt * * http://openbd.org/ * * $Id: $ */ package com.newatlanta.filters; import java.io.IOException; import java.util.StringTokenizer; import java.util.Vector; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; /* * SearchEngineFriendlyURLFilter * * This filter is used to allow BlueDragon to process search engine friendly * URLs. An example of this is /bdj2eedev/index.cfm/year/2006. This filter will * forward a request like this onto BlueDragon with a ServletPath of /index.cfm * and PathInfo of /year/2006. */ public final class SearchEngineFriendlyURLFilter implements Filter { private FilterConfig fConfig = null; private String[] extensions = null; private boolean debug = false; public SearchEngineFriendlyURLFilter() { super(); } /** * This method fixes bug #3275: "SearchEngineFriendlyURLFilter may sometimes match a request to the wrong alias" This method will also ignore duplicates, and strip off unwanted whitespace * * @param mappings * @param aliasToAdd */ private static void addAlias(Vector<String> mappings, String aliasToAdd) { int size = mappings.size(); int i = 0; aliasToAdd = aliasToAdd.trim(); for (; i < size; i++) { String storedAlias = mappings.elementAt(i); if (aliasToAdd.length() > storedAlias.length()) { mappings.insertElementAt(aliasToAdd, i); return; } else if (aliasToAdd.equals(storedAlias)) // The aliases are the same so do nothing return; } // The alias wasn't inserted so place it at the end if (i == size) mappings.addElement(aliasToAdd); } /** * This method takes an array Strings and converts it to a single String where each element is separated by a delimitor * * @param list * @param delimitor * @return */ public static String stringify(String[] list, String delimitor) { StringBuffer buf = new StringBuffer(); if (list != null) { for (int i = 0; i < list.length; i++) { if (i > 0) buf.append(delimitor); buf.append(list[i]); } } return buf.toString(); } /* * init * * Extracts and stores the configuration data for the filter. */ public void init(FilterConfig fConfig) throws ServletException { this.fConfig = fConfig; String debugStr = fConfig.getInitParameter("debug"); if ((debugStr != null) && (debugStr.equalsIgnoreCase("true"))) debug = true; logDebug("initializing"); Vector<String> v = new Vector(); String extensionsParam = fConfig.getInitParameter("extensions"); if (extensionsParam == null) { logDebug("using default extensions"); v.addElement("cfml"); v.addElement("cfm"); } else { logDebug("supplied extensions - " + extensionsParam); StringTokenizer st = new StringTokenizer(extensionsParam, ","); while (st.hasMoreTokens()) addAlias(v, st.nextToken()); } extensions = new String[v.size()]; v.toArray(extensions); logDebug("extensions will be matched to the request URI in the following order - [" + stringify(extensions, ",") + ']'); } /* * doFilter * * If the request contains an extension that the filter is configured to process and it contains path info then this method makes a request wrapper to return proper ServletPath and PathInfo values and forwards the request onto BlueDragon. */ public void doFilter(ServletRequest req, ServletResponse rsp, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpReq = (HttpServletRequest) req; String uri = httpReq.getRequestURI(); logDebug("filtering uri - " + uri); String foundExtension = null; int extensionPosition = -1; for (int i = 0; i < extensions.length; i++) { extensionPosition = uri.indexOf("." + extensions[i]); if ( extensionPosition != -1 ){ foundExtension = extensions[i]; } } if (foundExtension != null) { int servletPathEnd = extensionPosition + foundExtension.length() + 1; if (uri.length() != servletPathEnd) { logDebug("found extension match - " + foundExtension); // The URI contains an extension that the filter is configured to look // for and there's // path info so update the servletPath and pathInfo in a request // wrapper and let BD process it. int contextPathLen = httpReq.getContextPath().length(); if (contextPathLen == 1) contextPathLen = 0; String servletPath = uri.substring(contextPathLen, servletPathEnd); String pathInfo = uri.substring(servletPathEnd); ReqWrapper reqW = new ReqWrapper(httpReq, servletPath, pathInfo); RequestDispatcher rd = req.getRequestDispatcher(servletPath); rd.forward(reqW, rsp); return; } } // The URI doesn't contain an extension that the filter is configured // to look for or there's no path info so process it as it is. chain.doFilter(req, rsp); return; } /* * destroy * * Does nothing... */ public void destroy() { } /* * logDebug * * Logs debug message to System.out and ServletContext.log(). */ private void logDebug(String msg) { if (debug) { System.out.println("DEBUG SearchEngineFriendlyURLFilter: " + msg); fConfig.getServletContext().log("DEBUG SearchEngineFriendlyURLFilter: " + msg); } } /* * ReqWrapper * * This class is used to wrap the original request and to return proper values for ServletPath and PathInfo. */ class ReqWrapper extends HttpServletRequestWrapper { private String servletPath; private String pathInfo; public ReqWrapper(HttpServletRequest request) { super(request); } public ReqWrapper(HttpServletRequest request, String servletPath, String pathInfo) { super(request); this.servletPath = servletPath; this.pathInfo = pathInfo; } public String getServletPath() { return servletPath; } public String getPathInfo() { return pathInfo; } } }