How to use a library with its own filter and non-controller servlet in a Grails app?

848 Views Asked by At

I think the short, general form of my question might be the best: How do I get Grails/Spring to "see" dependency library components, including filters and servlets that are not Grails controllers?

Here are the specifics: I'd like to use the Togglz feature toggle library (v2.0.1) in my Grails (v2.4.4) application. Togglz comes with its own web console servlet and a TogglzFilter class that does some initialization at start-up and sets per-request state. In a vanilla spring web app, both of these are normally wired by annotations, so the url path [context-root]/togglz/index invokes the console. (The server is Pivotal tc/tomcat 7, which I believe satisfies the Servlet 3.0 requirement for the annotation-driven configuration to work.) My question is, how do I wire such a thing into my Grails application?

I'm new to both Grails and Togglz, so I'm probably missing something stupid. All the things I've tried produce a 404 on the console URL. So far, I have:

  • Added the togglz core, servlet, console, and spring dependencies in BuildConfig.groovy.
  • Added org.togglz and the package where I put my Togglz config (next two items) to the value of grails.spring.bean.packages in Config.groovy.
  • Created classes FeatureTogglz and FeatureTogglzConfig, both annotated with @Component.

That seemed like it should do it, but when it didn't, I tried a few more things:

  • Searching various combinations of Grails, Togglz, Library, Servlet, Filter, etc. produced two near misses:
    • In this thread, someone else was having trouble getting Grails to load Togglz. They were seeing not-found errors on start-up where I don't, and they were concerned with the actual applying of the togglz, not the console servlet. Also, the answer in that case was to remove the metadata-complete attribute from web.xml. My project has no web.xml at all.
    • In this thread, someone wanted to put a filter after a non-controller based response. In my case, activating the filter itself is the problem, not what kind of request/response it guards.
  • Attempted a UrlMappings.groovy entry using the console servlet class as the target:

    "/togglz/index"(org.togglz.console.TogglzConsoleServlet)`.
    

    I don't see any examples like this; I was just trying to guess what might accomplish what would otherwise be in web.xml.

  • Thinking maybe the problem was that Grails wasn't setting up the Togglz filter, which does some initialization at start-up in addition to setting the context for each request, I created conf/TogglzFilters.groovy, again guessing at the content:

    import org.togglz.servlet.TogglzFilter
    
    public class TogglzFilters {
        def filters = {
            togglzFilter(controller:'*', action:'*') {
                before = {
                    new TogglzFilter().doFilter(request, response, null)
                }
            }
        }
    }
    

    Even before it null-pointed, this felt totally wrong because what I want is to have Grails use the provided filter, not code it myself. But all the filter examples I could find used inline code.

As I said, much of this is new to me, so if I've just missed something I should have studied, I'd be as grateful for a pointer as for a direct answer.

Thanks.

2

There are 2 best solutions below

0
On

So there are two aspects to your question that have to be addressed.

First, the Spring configuration based on annotations.

Spring annotations from within the org.springframework.context.annotation package, such as @Configuration or even just @Bean won't be identified by Grails unless you tell it which packages to scan. This is done from within your Config.groovy with the following line:

// Config.groovy
grails.spring.bean.packages = ['the.package.name.goes.here']

In the above example my Spring components are in the package the.package.name.goes.here so you will need to update that with the packages for your components. You can specify multiple packages since the setting is a List of Strings.

Secondly, installing the servlet filter from your library. Now, this can be done any number of ways, from writing a plugin to do so for you, or installing it manually (which I will cover here).

To begin installing the servlet filter you first need to use install templates command to copy the templates used by Grails during code generation to your project. These templates will appear under src/templates. In this case we need to modify src/templates/war/web.xml to include the servlet filter.

Typically installing a filter looks something like the following:

<filter>
    <filter-name>MyFilter</filter-name>
    <filter-class>com.example.MyFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>MyFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

The exact requirements and settings for your servlet filter may differ slightly and I recommend you refer to the documentation for your library for them.

With the two above changes to your Grails application you should be able to leverage both the Spring annotation based configuration and servlet filter provided by your library.

Good luck and happy holidays!

0
On

If I had gotten the question right, the part of Joshua's answer about listing the bean packages would have been right. His recommendation to install templates and edit the resulting web.xml file turned out to be important also, but not to manually specify the filter.

Thanks Joshua. As it turned out, my suspicions of my own ignorance were well founded. Togglz doesn't use annotations at all. It uses web-fragment.xml files to define the console servlet and request filter. The problem turned out to be that we had already done the install-templates thing, and the web.xml Grails generated included metadata-complete="true", which was suppressing both annotation and web-fragment scanning. Editing the file to remove that attribute (it defaults to "false") was the key.