eclipse-ee4j / eclipse-ee4j/jersey

Overridden priority is not honoured

Open
#3,467 4 comments 1 reaction 0 assignees View on GitHub
Component: core Priority: Major Type: Bug
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.