Advertisement
Guest User

RoutesFilter.groovy

a guest
Aug 2nd, 2015
267
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Groovy 9.76 KB | None | 0 0
  1. /*
  2.  * Copyright 2009-2012 the original author or authors.
  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. package groovyx.gaelyk.routes
  17.  
  18. import groovy.servlet.AbstractHttpServlet
  19. import groovy.transform.CompileStatic
  20. import groovyx.gaelyk.GaelykBindingEnhancer
  21. import groovyx.gaelyk.GaelykServlet;
  22. import groovyx.gaelyk.GaelykTemplateServlet;
  23. import groovyx.gaelyk.cache.CacheHandler
  24. import groovyx.gaelyk.logging.GroovyLogger
  25. import groovyx.gaelyk.plugins.PluginsHandler
  26.  
  27. import java.util.concurrent.ConcurrentSkipListSet
  28.  
  29. import javax.servlet.Filter
  30. import javax.servlet.FilterChain
  31. import javax.servlet.FilterConfig
  32. import javax.servlet.ServletRequest
  33. import javax.servlet.ServletResponse
  34. import javax.servlet.http.HttpServletRequest
  35. import javax.servlet.http.HttpServletResponse
  36.  
  37. import org.codehaus.groovy.control.CompilerConfiguration
  38.  
  39. import com.google.appengine.api.NamespaceManager
  40. import com.google.appengine.api.utils.SystemProperty
  41.  
  42. /**
  43.  * <code>RoutesFilter</code> is a Servlet Filter whose responsability is to define URL mappings for your
  44.  * Gaelyk application. When the servlet filter is configured, a file named <code>routes.groovy</code>
  45.  * will be loaded by the filter, defining the various routes a web request may follow.
  46.  * <p>
  47.  * It is possible to customize the location of the routes definition file by using the
  48.  * <code>routes.location</code> init parameter in the declaration of the filter in <code>web.xml</code>.
  49.  * <p>
  50.  * In development mode, routes will be reloaded automatically on each request, but when the application
  51.  * is deployed on the Google cloud, all the routes will be set in stone.
  52.  *
  53.  * @author Guillaume Laforge
  54.  */
  55. class RoutesFilter implements Filter {
  56.  
  57.     static final String ORIGINAL_URI = 'originalURI'
  58.  
  59.     /**
  60.      * Location of the routes file definition
  61.      */
  62.     private String routesFileLocation
  63.     private long lastRoutesFileModification = 0
  64.     private SortedSet<Route> routes = new TreeSet<Route>()
  65.     private SortedSet<Route> routesFromRoutesFile = new TreeSet<Route>()
  66.     private FilterConfig filterConfig
  67.     private GroovyLogger log
  68.  
  69.     @CompileStatic
  70.     void init(FilterConfig filterConfig) {
  71.         this.filterConfig = filterConfig
  72.         this.routesFileLocation = filterConfig.getInitParameter("routes.location") ?: "WEB-INF/routes.groovy"
  73.         this.log = new GroovyLogger('gaelyk.routesfilter')
  74.         if (SystemProperty.environment.value() == SystemProperty.Environment.Value.Development) {
  75.             routes = new ConcurrentSkipListSet<Route>()
  76.             routesFromRoutesFile =  new ConcurrentSkipListSet<Route>()
  77.         }
  78.         loadRoutes()
  79.     }
  80.  
  81.     /**
  82.      * Load the routes configuration
  83.      */
  84.     synchronized void loadRoutes() {
  85.         log.config "Loading routes configuration"
  86.         routes.clear()
  87.         def routesFile = new File(this.routesFileLocation)
  88.  
  89.         if (routesFile.exists()) {
  90.             def lastModified = routesFile.lastModified()
  91.  
  92.             // if the file has changed since the last check, reload the routes
  93.             if (lastModified > lastRoutesFileModification) {
  94.                 def config = new CompilerConfiguration()
  95.                 config.scriptBaseClass = RoutesBaseScript.class.name
  96.  
  97.                 // define a binding for the routes definition,
  98.                 // and inject the Google services
  99.                 def binding = new Binding()
  100.                 GaelykBindingEnhancer.bind(binding)
  101.  
  102.                 // adds three nouns for the XMPP support
  103.                 binding.setVariable('chat',         'chat')
  104.                 binding.setVariable('presence',     'presence')
  105.                 binding.setVariable('subscription', 'subscription')
  106.  
  107.                 // evaluate the route definitions
  108.                 RoutesBaseScript script = (RoutesBaseScript) new GroovyShell(binding, config).parse(routesFile)
  109.  
  110.                 script.run()
  111.  
  112.                 routesFromRoutesFile.clear()
  113.                 List<Route> scriptRoutes = script.routes
  114.                 for(Route r in scriptRoutes){
  115.                     log.config "Adding route $r from routes file"
  116.                     routes.add r
  117.                     routesFromRoutesFile.add r
  118.                 }
  119.  
  120.                 // update the last modified flag
  121.                 lastRoutesFileModification = lastModified
  122.             } else {
  123.                 for(Route r in routesFromRoutesFile){
  124.                     log.config "Adding route $r from routes file"
  125.                     routes.add r
  126.                 }
  127.             }
  128.         }
  129.         // add the routes defined by the plugins
  130.         for(Route r in PluginsHandler.instance.routes){
  131.             log.config "Adding route $r from plugins"
  132.             routes.add r            
  133.         }
  134.     }
  135.  
  136.     /**
  137.      * Forward or redirects requests to another URL if a matching route is defined.
  138.      * Otherwise, the normal filter chain and routing applies.
  139.      */
  140.     void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) {
  141.         // reload the routes in local dev mode in case the routes definition has changed since the last request
  142.         try {
  143.             doFilterInternal(servletRequest, servletResponse, filterChain)            
  144.         } catch (Throwable t) {
  145.             throw filterStackTrace(servletRequest, t)
  146.         }
  147.     }
  148.  
  149.     private doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) {
  150.         if (SystemProperty.environment.value() == SystemProperty.Environment.Value.Development) {
  151.             loadRoutes()
  152.         }
  153.  
  154.         HttpServletRequest request = (HttpServletRequest)servletRequest
  155.         HttpServletResponse response = (HttpServletResponse)servletResponse
  156.  
  157.         if(!request.getAttribute(ORIGINAL_URI)){
  158.             request.setAttribute(ORIGINAL_URI, getIncludeAwareUri(request))
  159.         }
  160.  
  161.         def method = request.method
  162.  
  163.         boolean foundRoute = false
  164.         for (Route route in routes) {
  165.             // first, check that the HTTP methods are compatible
  166.             if (route.method == HttpMethod.ALL || route.method.toString() == method) {
  167.                 def result = route.forUri(getIncludeAwareUri(request), request)
  168.                 if (result.matches) {
  169.                     if (route.ignore) {
  170.                         // skip out completely
  171.                         break
  172.                     }
  173.                     if (route.redirectionType == RedirectionType.FORWARD) {
  174.                         if (route.namespace) {
  175.                             NamespaceManager.of(result.namespace) {
  176.                                 CacheHandler.serve(route, request, response)
  177.                             }
  178.                         } else {
  179.                             CacheHandler.serve(route, request, response)
  180.                         }
  181.                     } else if (route.redirectionType == RedirectionType.REDIRECT301) {
  182.                         response.setStatus(301)
  183.                         response.setHeader("Location", result.destination)
  184.                         response.setHeader("Connection", "close")
  185.                     } else {
  186.                         response.sendRedirect result.destination
  187.                     }
  188.                     foundRoute = true
  189.                     break
  190.                 }
  191.             }
  192.         }
  193.  
  194.         if (!foundRoute) {
  195.             filterChain.doFilter servletRequest, servletResponse
  196.         }
  197.     }
  198.  
  199.     @CompileStatic
  200.     void destroy() { }
  201.  
  202.     /**
  203.     * Returns the include-aware uri.
  204.     *
  205.     * @param request the http request to analyze
  206.     * @return the include-aware uri either parsed from request attributes or
  207.     *         hints provided by the servlet container
  208.     */
  209.     @CompileStatic
  210.     static String getIncludeAwareUri(HttpServletRequest request) {
  211.         String uri = null
  212.         String info = null
  213.  
  214.         uri = request.getAttribute(AbstractHttpServlet.INC_SERVLET_PATH)
  215.         if (uri != null) {
  216.             info = request.getAttribute(AbstractHttpServlet.INC_PATH_INFO)
  217.             if (info != null) {
  218.                 uri += info
  219.             }
  220.             return uri
  221.         }
  222.  
  223.         uri = request.getServletPath()
  224.         info = request.getPathInfo()
  225.         if (info != null) {
  226.             uri += info
  227.         }
  228.         return uri
  229.     }
  230.    
  231.     static <T extends Throwable> T filterStackTrace (ServletRequest request, T original) {
  232.         if (request.getParameter('stacktrace') == 'true') {
  233.             return original
  234.         }
  235.         Throwable cause = original
  236.         while (cause) {
  237.             boolean ignoreRest = false
  238.             cause.stackTrace = cause.stackTrace.findAll { StackTraceElement el ->
  239.                 if (ignoreRest) return false
  240.                 if (el.getClassName() in [GaelykServlet, GaelykTemplateServlet]*.name) {
  241.                     ignoreRest = true
  242.                     return false
  243.                 }
  244.                 for (String packageName in GroovyLogger.EXCLUDE_LIST) {
  245.                     if (el.className.startsWith(packageName)) {
  246.                         return false
  247.                     }
  248.                 }
  249.                 return true
  250.             }.toArray()
  251.            
  252.             cause = cause.cause
  253.         }
  254.         return original
  255.     }
  256. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement