6

I have been researching how to perform URL rewrites on Tomcat 8 and keep running into the same two suggestions.

1) Use Tuckey URLRewriteFilter. 2) Run Apache on top of Tomcat to use mod_rewrite.

In regards to the former, URLRewriteFilter doesn't appear to have any documentation about how to configure in a Java format as opposed to XML. My Spring MVC application does not make use of a web.xml file - all configuration is done via Java classes - and so I'm not in a position to configure with XML.

Is there any way to configure in this way or are there any good alternatives other than trying to run Apache on top of Tomcat?

For example, is there a way to achieve this in Java as opposed to XML:

<filter>
   <filter-name>UrlRewriteFilter</filter-name>
   <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>UrlRewriteFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>

This is my WebApplicationInitializer class:

public class Initializer implements WebApplicationInitializer {

@Override
public void onStartup(ServletContext container) {
  // Create the 'root' Spring application context
  AnnotationConfigWebApplicationContext rootContext =
    new AnnotationConfigWebApplicationContext();
  rootContext.register(RootConfig.class);

  // Manage the lifecycle of the root application context
  container.addListener(new ContextLoaderListener(rootContext));

  // Create the dispatcher servlet's Spring application context
  AnnotationConfigWebApplicationContext dispatcherContext =
    new AnnotationConfigWebApplicationContext();
  dispatcherContext.register(WebAppConfig.class);

  // Register and map the dispatcher servlet
  ServletRegistration.Dynamic dispatcher =
    container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
  dispatcher.setLoadOnStartup(1);
  dispatcher.addMapping("/*");
  dispatcher.addMapping("*.html");
} 

}

My RootConfig

@Configuration
@ComponentScan(basePackages={"com.ucrisko.*"},
    excludeFilters={
      @ComponentScan.Filter(type=FilterType.ANNOTATION, value=EnableWebMvc.class)
    })
public class RootConfig {

}

And my WebAppConfig

@Configuration
@EnableWebMvc
@ComponentScan(basePackages={"com.ucrisko.*"})
public class WebAppConfig extends WebMvcConfigurerAdapter{

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}

...various other beans
}
Ravioli87
  • 795
  • 13
  • 34

1 Answers1

6

In Spring Boot you can just define filter as a bean in your configuration. I'm not sure if that works in plain Spring MVC but you can try. One awkward thing is configuring the filter from java code - you will have to implement FilterConfig. A working Spring Boot example looks like this (snippet from the config class):

@Bean
public UrlRewriteFilter urlRewriteFilter(final ServletContext servletContext) throws ServletException {
    UrlRewriteFilter urlRewriteFilter = new UrlRewriteFilter();
    urlRewriteFilter.init(new FilterConfig() {
        private final Map<String, String> params = new HashMap<String, String>();
        {
            params.put("confPath", "urlrewrite.xml");
        }

        @Override
        public String getFilterName() {
            return "UrlReriteFilter";
        }

        @Override
        public ServletContext getServletContext() {
            return servletContext;
        }

        @Override
        public String getInitParameter(String name) {
            return params.get(name);
        }

        @Override
        public Enumeration<String> getInitParameterNames() {
            return Collections.enumeration(params.keySet());
        }
    });

    return urlRewriteFilter;
}

As for regular Spring MVC, it seems you should implement WebApplicationInitializer. WebApplicationInitializer is java code equivalent of web.xml configuration. According to documentation, Spring should pick up the implementations from the classpath and run them. Sample implementation may look like this (Note: I haven't tested if this works, but it should set you on the right path):

public class Initalizer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return null;
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] {WebAppConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] {"/*", "*.html"};
    }

    @Override
    protected Filter[] getServletFilters() {
        UrlRewriteFilter urlRewriteFilter = new UrlRewriteFilter();
        /*
         * Add filter configuration here if necessary
         */
        return new Filter[] {urlRewriteFilter};
    }
}

AbstractAnnotationConfigDispatcherServletInitializer is Spring's implementation of WebApplicationInitializer interface with intention to make initialization of DispcherServlet (which is entry point Spring MVC) easier. In this example WebAppConfig.class is your main Spring java config class.

EDIT

If you prefer to keep your current initializer code, it should be also posssible to add following lines in onStartup method:

FilterRegistration.Dynamic urlRewriteFilter = servletContext.addFilter("urlRewriteFilter",  new UrlRewriteFilter());
urlRewriteFilter.setInitParameter("confPath", "urlrewrite.xml");
urlRewriteFilter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD), false, "/*");

This snipped is based on an answer available here: How to do filter mapping in AbstractAnnotationConfigDispatcherServletInitializer Spring

Community
  • 1
  • 1
Krešimir Nesek
  • 5,302
  • 4
  • 29
  • 56
  • The top portion of your answer seems to work at least partially. In my server log I get org.apache.catalina.core.ApplicationContext.log org.tuckey.web.filters.urlrewrite.UrlRewriteFilter INFO: loaded (conf ok) and it seems to be able to locate my urlrewrite.xml file. However, I don't end up with any URLs being rewritten. For example, the following does not end up with an attempted redirect to the index.html page. ^users index.html – Ravioli87 Oct 05 '15 at 17:46
  • If nothing is happening then Spring probably just instantiates the UrlRewriteFilter class (causing the log messages you see) but it doesn't actually register the filter (in other words - it doesn't work). What happens if you try with bottom approach? How are you starting your Spring MVC application? (e.g. do you have a class that implements WebApplicationInitializer interface? Where do you specify your main javaconfig configuration class?) – Krešimir Nesek Oct 05 '15 at 18:58
  • Yes, I do have a WebApplicationInitializer but it looks a bit different than what you have. I will add it to my question above. – Ravioli87 Oct 05 '15 at 19:14
  • I edited the code sample to match your current `WebApplicationInitializer`. `AbstractAnnotationConfigDispatcherServletInitializer` seems to be Spring's preferred way to initialize `DispatcherServlet` and it also makes registering filters easier. If you prefer to keep your own implementation, then take a look at the source code of `AbstractAnnotationConfigDispatcherServletInitializer` and take a look how it registers the filters using `getServletFilters` – Krešimir Nesek Oct 05 '15 at 20:27
  • I also edited my answer to include code that should applicable to your current `WebApplicationInitializer` – Krešimir Nesek Oct 05 '15 at 20:51
  • Awesome! Both of these work. I actually got pretty close to the solution with FilterRegistration.Dynamic but didn't configure it properly initially. I will take your advice and switch my Initializer class to the format you have here. Thanks for your help! – Ravioli87 Oct 05 '15 at 20:53