4

DelegatingFilterProxy has two constructors that I can use in my application

new DelegatingFilterProxy(String beanName);
new DelegatingFilterProxy(Filter delegate);

In my webappIntializer, I have the following. (please see below for full code).

servletContext.addFilter("counterMetricsFilter", new DelegatingFilterProxy("counterMetricsFilter").addMappingForUrlPatterns(null,
                false, MAPPING_URL);

I am not sure if DelegatingFilterProxy is functioning as expected in my case.

Controller

@Controller
@RequestMapping("/counter")
public class CounterController {

@Autowired
CounterManager manager;

 @RequestMapping(value = "/counterMetrics", method = RequestMethod.GET)
    @ResponseBody
    public Map getFeatureStatus(final HttpServletResponse response) {
        System.out.println("returning feautre status");
        return manager.getQueryCounts();
    }
}

Manager

@Service
public class CounterManager {

private Map<String,Integer> queryCounts =
            new ConcurrentHashMap<String,Integer>(1000);

int threshold;
long timestamp;

 @Autowired
  public CounterManager(@Value("${healthThreshold: 10}")
                                 int threshold) {
      this.threshold = threshold * MEGABYTES;
      this.timestamp = System.currentTimeMillis();
  }


 public void incrementRequestCounter(String urlPath){
     Integer oldVal, newVal;
     do {
       oldVal = queryCounts.get(model);
       newVal = (oldVal == null) ? 1 : (oldVal + 1);
     } while (!queryCounts.replace(model, oldVal, newVal));
 }

public Map<StatusUrlModel, Integer> getQueryCounts() {
    return queryCounts;
}

}

Filter

@Order(Ordered.HIGHEST_PRECEDENCE)
@Component(value="counterMetricsFilter")
public class CounterMetricsFilter extends OncePerRequestFilter {

    @Autowired
    CounterManager manager;

    @Override
    protected void doFilterInternal(HttpServletRequest request,
            HttpServletResponse response, FilterChain chain) throws ServletException,
            IOException {
        String path = new UrlPathHelper().getPathWithinApplication(request);
        try {
            chain.doFilter(request, response);
        }
        finally {
            recordMetrics(request, path);
        }
    }

   private void recordMetrics(String path) {
        try {
        manager.incrementRequestCounter(url);
        }
        catch (Exception ex){
            System.out.println("exception is thrown " + ex.getCause());
            ex.printStackTrace();
        }
    }
}

DelegatingFilterProxy

public class myAppWebAppInitializer implements WebApplicationInitializer {

             private static final String CONFIG_LOCATION = "com.myapp.sample.config";
            public static final String MAPPING_URL = "/*";

            @Override
            public void onStartup(ServletContext servletContext) throws ServletException {

     AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
    context.setConfigLocation(CONFIG_LOCATION);

                servletContext.addListener(new ContextLoaderListener(context));

                servletContext.addFilter("counterMetricsFilter", new DelegatingFilterProxy("counterMetricsFilter").addMappingForUrlPatterns(null,
            false, MAPPING_URL);

                ServletRegistration.Dynamic dispatcher = servletContext.addServlet("DispatcherServlet", new DispatcherServlet(
                        context));
                dispatcher.setLoadOnStartup(1);
                dispatcher.addMapping(MAPPING_URL);
            }

    }

NOTE: The container start-up fine. Requests go through fine. because this line manager.incrementRequestCounter(url) in CounterMetricsFilter above is throwing exception, and I am catching it, I see empty response. I suspect delegateFilterProxy is not autowiring properly.

Also Note that manager bean is loaded in Application Context because I can inspect the bean in CounterController.

I am unable to fix it. Any tips here would be very helpful.

EDIT

by changing to constructor injection instead of field injection in CounterMetricsFilter, I get the following exception.

//I removed the no-arg constructor above @Autowired public CounterMetricsFilter(HealthManager healthManager){ this.healthManager=healthManager; }

stacktrace

SEVERE: Exception starting filter counterMetricsFilter
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'counterMetricsFilter' is defined
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:638)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1159)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:282)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:979)
    at org.springframework.web.filter.DelegatingFilterProxy.initDelegate(DelegatingFilterProxy.java:324)
    at org.springframework.web.filter.DelegatingFilterProxy.initFilterBean(DelegatingFilterProxy.java:235)
    at org.springframework.web.filter.GenericFilterBean.init(GenericFilterBean.java:199)
    at org.apache.catalina.core.ApplicationFilterConfig.initFilter(ApplicationFilterConfig.java:281)
    at org.apache.catalina.core.ApplicationFilterConfig.<init>(ApplicationFilterConfig.java:111)
    at org.apache.catalina.core.StandardContext.filterStart(StandardContext.java:4775)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5452)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:633)
    at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1113)
    at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:1671)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
  [1]: http://stackoverflow.com/questions/32573742/why-is-autowired-bean-null-in-filter-in-this-spring-application
brain storm
  • 30,124
  • 69
  • 225
  • 393
  • 1
    possible duplicate of [Why is my Spring @Autowired field null?](http://stackoverflow.com/questions/19896870/why-is-my-spring-autowired-field-null) – chrylis -cautiouslyoptimistic- Sep 15 '15 at 18:19
  • @chrylis: this is not a duplicate. – brain storm Sep 15 '15 at 18:41
  • Sure looks like it to me. Your filter isn't a Spring bean. – chrylis -cautiouslyoptimistic- Sep 15 '15 at 18:43
  • @chrylis: my filter is a spring bean. I have `@component` – brain storm Sep 15 '15 at 18:45
  • Do you have a web context and a parent application context? In which one is your filter? – fps Sep 15 '15 at 18:48
  • And if you'll read my explanation, just slapping `@Component` on a class doesn't make every instance of it a Spring bean. You have two instances of your class, one that's a bean and one that's actually used as a filter. – chrylis -cautiouslyoptimistic- Sep 15 '15 at 18:52
  • @chrylis: I am sorry. I can't find where I make instances. can you please point me the line. As far as see, I have one `@Component` bean with name and using it everywhere. I am missing to see something that you are seeing. please point me to that. I will close this – brain storm Sep 15 '15 at 18:58
  • I misread your configuration on my phone and confused it for a Servlet 3 configuration. I *think* this should work. I recommend replacing your field injection with constructor injection and seeing if you get notified of errors somewhere. – chrylis -cautiouslyoptimistic- Sep 15 '15 at 19:14
  • @chrylis: I tried construction injection. I get `NoSuchBeanDefinitionException`. I have updated the post with stacktrace – brain storm Sep 15 '15 at 19:29
  • This line: `servletContext.addListener(new ContextLoaderListener(context));` is used to load parent application context. The `DelegatinFilterProxy` belongs to the web context, but your `counterMetricsFilter` filter must be declared in the wrong context. If it's in parent app context, try changing it to web app context; or change it to parent app context if it's declared in the web context. (I don't remember now which is the right context for bean filters). You could start by just commenting this line: `servletContext.addListener(new ContextLoaderListener(context));` and see what happens. – fps Sep 15 '15 at 20:09
  • @FedericoPeraltaSchaffner: I cannot comment out `servletContext.addListener(new ContextLoaderListener(context))`. I have other filters configured apart from `DelegatingFilterProxy` – brain storm Sep 15 '15 at 20:12
  • @brainstorm I understand. If you're using one package for everything (both parent app context and web context), then you're screwed... Try moving your `CounterMetricsFilter` bean to a package that is scanned only when one context is loading. I'm confident that you're loading your web beans also in the parent context, however filter beans must be present in one context only, and I believe it is in the web context... – fps Sep 15 '15 at 20:15
  • @FedericoPeraltaSchaffner: I don't think I am following you. I have only context in my entire application which I have cited above. I don't see where parent and web context comes from or where a distinction is seen. if you can provide a clear example in answer, it will be great – brain storm Sep 15 '15 at 20:28
  • @brainstorm I put all this in comments because I'm not sure it will work ;) I remember we had these issues a long time ago, and removing that line fixed everything. The `ContextLoaderListener` was evil ;) – fps Sep 15 '15 at 20:29
  • I have only context in my application..commenting out contextLoadListener throws exception `No WebApplicationContext found: no ContextLoaderListener registered?`. – brain storm Sep 15 '15 at 20:39

1 Answers1

9

I don't see configuration class itself among the code snippets above, so listing the general solution how to make DelegatingFilterProxy work with Spring Java Config.

Initializer

public class MyWebAppInitializer implements WebApplicationInitializer {
    ...
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        ...
        servletContext.addFilter("myFilter", new DelegatingFilterProxy("myFilter"))
                .addMappingForUrlPatterns(null, true, "/*");
        ...
    }
    ...
}

Filter

public class MyFilter implements Filter {
    ...
    // I added this parameter just as example of how Spring-managed dependencies
    // could be injected. Note, there is no any annotations in this class.
    private String someParameter;
    public MyFilter(String someParameter) {
        this.someParameter = someParameter;
    }
    ...
}

Configuration

@Configuration
@EnableWebMvc
@PropertySource(value = {
        "classpath:my.properties"
})
@Import({
        PropertySourcesPlaceholderConfigurer.class
})
public class MyConfig {
    ...
    @Bean
    public MyFilter myFilter(@Value("${someParameter}") String someParameter) {
        return new MyFilter(someParameter);
    }
    ...
}

Hope it will be useful for someone.

bsiamionau
  • 8,099
  • 4
  • 46
  • 73
  • Hi @bsiamionau, starting from Spring Boot 2.6 circular dependencies are prohibited by default. If I declare the MyFilter Bean following your answer I'm getting a self-circular dependency error, do you know how to resolve this? – desoss Feb 06 '23 at 15:20
  • there is just a single bean - `myFilter`, it doesn't point to other beans. could you please clarify where does dependency appear? – bsiamionau Feb 07 '23 at 09:00