eclipse-ee4j / eclipse-ee4j/jersey
Overridden priority is not honoured
- Dominant language
- Java
- Stars
- 730
- Forks
- 382
- PR merge metrics
- No merged PRs in 30d
Description
Due to filter ordering issues (especially with a CORSFilter that _aborts_ and the jersey-spring3 RequestContextFilter), I want to order the filters with determinist priorities.
The javadoc of ResourceConfig.register(Class componentClass, int bindingPriority) says :
> Description copied from interface: javax.ws.rs.core.Configurable Register a class of a custom JAX-RS component (such as an extension provider or a feature meta-provider) to be instantiated and used in the scope of this configurable context.
> This registration method provides the same functionality as register(Class) except that any priority specified on the registered JAX-RS component class via javax.annotation.Priority annotation is overridden with the supplied priority value.
> Note that in case the priority is not applicable to a particular provider contract implemented by the class of the registered component, the supplied priority value will be ignored for that contract.
With that information I crafted the following configuration :
```
**EndointsConfig**@ApplicationPath("/")
public class EndpointsConfig extends ResourceConfig {
public EndpointsConfig(@Context ServletContext context) {
WebApplicationContext webAppContext = WebApplicationContextUtils.getWebApplicationContext(context);
this.property(WADL_FEATURE_DISABLE, true)
.register(MultiPartFeature.class)
.register(webAppContext.getBean(CORSFilter.class), 500)
.register(RequestContextFilter.class, -500)
.register(JacksonJsonProvider.class)
.register(StreamableObjectWriter.class)
.register(new LoggingFeature(Logger.getLogger("http"), PAYLOAD_TEXT))
// ...
;
}
}
```
```
**CORSFilter**@Provider
@PreMatching
@Priority(0)
public class CORSFilter implements ContainerRequestFilter, ContainerResponseFilter {
// ... }
```
```
**org.glassfish.jersey.server.spring.scope.RequestContextFilter**@Provider
@PreMatching
public final class RequestContextFilter implements ContainerRequestFilter, ContainerResponseFilter {
// ... }
```
However the CORSFilter is always executed before the Spring related filter.
While debugging I found out that providers is sorted according to an internal rank
```
Unable to find source-code formatter for language: containerfilteringstage.apply. Available languages are: actionscript, html, java, javascript, none, sql, xhtml, xml public Continuation apply(RequestProcessingContext context) {
// ...
if (postMatching) {
// ...
} else {
// pre-matching (response filter stage is pushed in pre-matching phase, so that if pre-matching filter
// throws exception, response filters get still invoked)
context.push(new ResponseFilterStage(context, responseFilters, tracingLogger));
sortedRequestFilters = Providers.sortRankedProviders(new RankedComparator(), requestFilters);
}
```
However the sorted filters does not match what I have defined in the EndpointsConfig.
Indeed looking at the rank value I see that overridden binding priority values are not there. Instead Values from the annotation are used.
```
**RankedProvider.computeRank**private int computeRank(final T provider, final int rank) {
if (rank > 0) {
return rank;
} else {
if (provider.getClass().isAnnotationPresent(Priority.class)) {
return provider.getClass().getAnnotation(Priority.class).value();
} else {
return Priorities.USER;
}
}
}
```
While debugging I have found that **all** providers are created with the rank value 0\. That rank value comes from HK2 ActiveDescriptor.getRanking, and is used to create the RankedProvider as shown below :
```
**Providers.getAllRankedProviders** public static Iterable> getAllRankedProviders(final ServiceLocator locator, final Class contract) {
final List> providers = getServiceHandles(locator, contract, CustomAnnotationLiteral.INSTANCE);
providers.addAll(getServiceHandles(locator, contract));
final LinkedHashMap, RankedProvider> providerMap =
new LinkedHashMap, RankedProvider>();
for (final ServiceHandle provider : providers) {
final ActiveDescriptor key = provider.getActiveDescriptor();
if (!providerMap.containsKey(key)) {
final Set contractTypes = key.getContractTypes();
final Class implementationClass = key.getImplementationClass();
boolean proxyGenerated = true;
for (Type ct : contractTypes) {
if (((Class) ct).isAssignableFrom(implementationClass)) {
proxyGenerated = false;
break;
}
}
providerMap.put(key,
new RankedProvider(provider.getService(), key.getRanking(), proxyGenerated ? contractTypes : null));
}
}
return providerMap.values();
}
```
I'm a bit lost when debugging the code between the actual configuration, and the initialisation phase here. Yet I think this is a fault behaviour according the the javadoc.
The only solution to counter that is to wrap these filters in a wrapping class annotated with the wanted binding priority.
#### Affected Versions
[2.24.1]
Contributor guide
Research direction
Trace registration from EndpointsConfig through Providers.getAllRankedProviders and RankedProvider.computeRank, then inspect ContainerFilteringStage.apply where filters are sorted. Reproduce the CORSFilter priority 500 and RequestContextFilter priority -500 configuration; done means the overridden binding priorities, rather than annotation values, determine filter ordering.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, spring
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 32/100