I have a code generator, that generates interfaces for JAX-RS endpoints and my backend app implements these interfaces, to provide the business logic.
The problem now is, that I cannot use name-bound container filters to enhance the business logic or to add security: Any @NameBinding marker annotation on the implementing class or its methods is ignored and the corresponding filter is not called.
Here is a minimal example: (code is in Kotlin, but the issue is the same when implemented in pure Java)
// generated
data class FooDto(val filtered: Boolean)
// generated
@Path("/")
interface OpenApiGeneratedInterface {
@GET
@Path("/foo/bar")
@Produces("application/json")
fun foo(): FooDto
}
// my implementation
class ImplementingApiController : OpenApiGeneratedInterface {
@TestMarker
override fun foo() = FooDto(filtered = false)
}
// may come from external dependency
@NameBinding
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class TestMarker
// may come from external dependency
@Provider
@TestMarker
class TestFilter: ContainerResponseFilter {
override fun filter(
requestContext: ContainerRequestContext,
responseContext: ContainerResponseContext,
) {
responseContext.entity = FooDto(filtered = true)
}
}
When a request to /foo/bar is made, I get {"filtered":false}, so the filter is not running.
When I move the @TestMarker annotation from ImplementingApiController::foo to OpenApiGeneratedInterface::foo, I instead get {"filtered":true}, so the filter did run this time. Note, that modifying the interface is not possible in reality, since the real interfaces are generated. I only did this in the example to show that the filter is working in general.
The problem seems to be, that the system only looks for marker annotations on the interfaces and never on the implementing classes.
Here is the complete picture; I am in control of:
- The
ImplementingApiControllerclass - The system that the app is running on (so I can change the configuration or add more filters/interceptors)
I have no or nearly no control over:
- The
OpenApiGeneratedInterfaceinterface (is generated from an OpenAPI spec) - The DTO classes like
FooDto(also generated) - The code generator that creates these interfaces (it is a distant project)
- The
@TestMarkerannotation and its corresponding filter (come from yet another project)
This leaves me with little wiggle room to get this working.
Is this even possible in this constellation, and if yes, how would this work?
What I have tried so far:
- Add a
@Pathor@Providerannotation toImplementingApiControllerto force the system to use this class for annotation discovery (did not work) - Add a
javax.ws.rs.container.DynamicFeatureand wire up the filters by searching the interface implementations via reflection (could work, but it will get really ugly, when interface and implementation are not managed by the same class loader) - Add my own
ContainerResponseFilterthat is always active and call the actual filters dynamically (also requires the same reflection madness like with aDynamicFeature)
Further ideas:
- Change the code generator to omit the JAX-RS annotations in the interface and annotate everything myself (works, but almost completely defeats the point)
- Change the code generator to include all kinds of marker annotations that I need (then I run into cyclic dependency problems when building the generated code)