Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce @ConditionalOnMissingServletFilter #7654

Closed
wants to merge 2 commits into from

Conversation

bclozel
Copy link
Member

@bclozel bclozel commented Dec 15, 2016

These commits introduce the @ConditionalOnMissingServletFilter
and make use of it to register ResourceUrlEncodingFilter
instances as FilterRegistrationBeans instead of beans.

This new conditional annotation is borrows a bit from the bean
conditions but also checks for FilterRegistrationBean and the
type of filter they're about to register.
This requires getting the actual bean, which is obviously less
efficient than the checks involved in bean conditions. I've
configured that condition to have a low precedence, but I'm
wondering if more checks/guards should be added here.

The second commit applies that new Condition to existing filters
and turns them into FilterRegistrationBean to finally achieve
the original goal: configure ResourceUrlEncodingFilters for
all dispatcher types and make them available during error
dispatches, so as to properly render URLs in error pages.

}
});
}
catch (ClassNotFoundException ex) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably require a guard for NoClassDefFoundError as well.

BeanTypeRegistry beanTypeRegistry = BeanTypeRegistry.get(context.getBeanFactory());
try {
Map<String, FilterRegistrationBean> filterRegistrations = context
.getBeanFactory().getBeansOfType(FilterRegistrationBean.class);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't there other ways to register a Filter?

}
if (servletFilters.isEmpty() &&
metadata instanceof MethodMetadata && metadata.isAnnotated(Bean.class.getName())) {
addDeducedBeanTypeForBeanMethod(context, metadata, servletFilters, (MethodMetadata) metadata);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is only a single use where the value is omitted. Perhaps it's not a frequent use case? If it isn't, maybe it would be nicer to just not support it and remove all the code that deduces the type?

This commit introduces a new Conditional annotation
`@ConditionalOnMissingServletFilter` which checks for the absence of
Servlet filters as beans or `FilterRegistrationBean`s.

This Conditional annotation can be used to guard Servlet Filter bean
definitions like:

```
@bean
@ConditionalOnMissingServletFilter(MyServletFilter.class)
public MyServletFilter myServletFilter() {
  //...
}
```

Fixes spring-projectsgh-7475
This commit registers all `ResourceUrlEncodingFilter` instances as
`FilterRegistrationBean`s instead of regular beans, in order to register
those for all Servlet dispatcher types (and make them available to error
dispatches).

To avoid duplicate registration of those filters, this commit is relying
on the `@ConditionalOnMissingServletFilter` annotation that checks for
both beans and filter registration beans.

Fixes spring-projectsgh-7348
@bclozel
Copy link
Member Author

bclozel commented Dec 15, 2016

After a first round of review and discussion with @snicoll, here are a few remaining questions before we can introduce this new Condition:

  1. @ConditionalOnMissingServletFilter is somehow a specialized version of @ConditionalOnMissingBean and right now it does only consider the current context. Should we search in parent context (and introduce the concept of SearchStrategy)?
  2. This condition searches at first for filter beans of the given type(s) and get all available FilterRegistrationBeans and check what filter type they're registering. Should we stop at the first matching bean and avoid expensive checks (like bean initialization for the registration beans) or should keep the current version and have a condition message listing all the possible matching beans?

@wilkinsona, @philwebb - I'd really like to get your opinion on those questions. Thanks!

@wilkinsona
Copy link
Member

  1. Yes, I think we should
  2. While we definitely need to check for a matching FilterRegistrationBean, doing so makes me nervous as it may trigger early initialisation. Given the nature of a typical FilterRegistrationBean that's unlikely to cause a problem, but it could do. I'm not sure what the best answer is here.

@philwebb
Copy link
Member

It's quite tricky. FilterRegistrationBeans are instantiated quite early by the EmbeddedWebApplicationContext but that alone has cause some issues. Getting them even earlier when the condition runs is likely to cause other problems. The condition also isn't considering DelegatingFilterProxyRegistrationBean registrations.

In hindsight, we should have probably made FilterRegistrationBean generic. That way we could inspect the method signature for FilterRegistrationBean<ResourceUrlEncodingFilter> items. We could consider this for 2.0.

I wonder if we can fix this a different way by registering a variant of ResourceUrlEncodingFilter that checks for other beans on the first call to doFilterInternal? That way we wouldn't need to try and detect ResourceUrlEncodingFilter beans early and instead always register our filter but simply back off if it's not needed.

@bclozel bclozel added this to the 2.0.0.M1 milestone Dec 16, 2016
@bclozel bclozel added the type: enhancement A general enhancement label Dec 16, 2016
@bclozel bclozel removed this from the 2.0.0.M1 milestone Dec 16, 2016
@bclozel bclozel removed the type: enhancement A general enhancement label Dec 16, 2016
@wilkinsona
Copy link
Member

wilkinsona commented Dec 16, 2016

Perhaps we could avoid the problem by making the auto-configuration of both the filter and its registration bean conditional on their being no existing filter bean. That would allow the user to override our auto-configuration either by providing their own filter bean (and using the registration bean's defaults) or by providing their own filter bean and a registration bean.

The generic enhancement in 2.0 would then allow you to override an auto-configured filter by just providing a registration bean, i.e. you wouldn't need to expose the filter as a bean as well.

@bclozel
Copy link
Member Author

bclozel commented Dec 16, 2016

I've taken a step back and looked at the original issue #7348; I don't think this is worth making the current (auto-)configurations more complex than they are and worse, risking failing application initializations.

I've moved the dedicate issues to 2.0.0.M1 and created #7666 to make FilterRegistrationBean generic. Looking at DelegatingFilterProxyRegistrationBean, I don't know how we could do that or if that condition should be used to guard against those types of filter declaration.

@wilkinsona
Copy link
Member

It looks like the need for this has passed. We can always resurrect this if that turns out not to be the case.

@wilkinsona wilkinsona closed this Feb 2, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants